NOIP提高模拟-20181017-T2-管道(状压DP)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sdsy191553/article/details/83114935

写在前面

本来写了一个DFS强行暴力一发,然而DFS是错的,我写的DFS和标算的方法大相径庭,愉快爆零。

Solution

20 20 ~ 40 p t s 40pts 做法
直接暴搜即可。

60 60 ~ 100 p t s 100pts 做法
搜索+记忆化。

100 p t s 100pts 做法详细解析
d p i , s t a dp_{i,sta} 表示当前在 i i 点,已访问过的点集为 s t a sta 时,将剩余所有能访问得到的点访问完后的总方案数。然而,似乎无法直接维护 d p dp 数组,因为在搜索的时候,剩下的所有能访问到的点的状态还未被计算,而在一张图上,又不能用循环进行状态转移。所以说我们需要借助一个辅助数组 f f
f i , s t a f_{i,sta} 表示当前在 i i 点,已访问过的点集为 s t a sta 时,访问剩余所有能访问得到的点后的状态,有了这个数组,我们就可以在计算 d p dp 数组时利用 f f 数组已经计算出来的状态,来更新 d p dp 数组。
又因为是无向图,所以 d f s dfs 一个点以后会访问完所有与它联通的点才会回到这个点,那么我们可以得到转移方程:
d p i , s t a = j d p j , s t a 2 j d p i , s t a f j , s t a dp_{i,sta}=\sum_j dp_{j,sta|2^{j}}*dp_{i,sta}|f_{j,sta}
对于 f f ,可以直接 d f s dfs 求,时间复杂度 O ( n 2 2 n ) O(n^22^n)
Talk is Cheap, Show You the Code:

#include<bits/stdc++.h>
using namespace std;
const int N=20;
const int mod=998244353;
const int M=450;
int read(){
	int sum=0,neg=1;
	char c=getchar();
	while(c>'9'||c<'0'&&c!='-') c=getchar();
	if(c=='-') neg=-1,c=getchar();
	while(c>='0'&&c<='9') sum=(sum<<1)+(sum<<3)+c-'0',c=getchar();
	return sum*neg;
}
int n,m;
struct Edge{
	int u,v;
}e[M<<1];
int first[M],nxt[M],cnt=0;
void AddEdge(int u,int v){
	e[++cnt].u=u;e[cnt].v=v;
	nxt[cnt]=first[u]; first[u]=cnt;
}
int f[1<<N][N];
int dfs1(int sta,int u){
	if(f[sta][u]) return f[sta][u];
	f[sta][u]=sta;
	for(int i=first[u];i;i=nxt[i]){
		int v=e[i].v;
		if(!(sta&(1<<v))) f[sta][u]|=dfs1(sta|(1<<v),v);//没有访问过
			
	}
	return f[sta][u];
}
long long dp[1<<N][N];
int dfs(int sta,int u){
	if(f[sta][u]==sta) return 1;
	if(dp[sta][u]!=-1) return dp[sta][u];
	dp[sta][u]=0;
	for(int i=first[u];i;i=nxt[i]){
		int v=e[i].v;
		if(sta&(1<<v)) continue;
		int x=dfs(sta|(1<<v),v);
		int y=dfs(f[sta|(1<<v)][v],u);
		dp[sta][u]+=(long long)x*y%mod;
	}
	return dp[sta][u]%=mod;
}
int main(){
	n=read(); m=read();
	for(int i=1;i<=m;i++){
		int u,v;
		u=read()-1; v=read()-1;
		AddEdge(u,v);
		AddEdge(v,u);
	}
	for(int i=0;i<(1<<n);i++)
		for(int j=0;j<n;j++)
			if(i&(1<<j)) f[i][j]=dfs1(i,j);//i表示全图访问状态的一个子集
	memset(dp,-1,sizeof(dp));
	long long ans=0;
	for(int i=0;i<n;i++) ans+=dfs(1<<i,i);
	printf("%lld\n",ans%mod);
}

猜你喜欢

转载自blog.csdn.net/sdsy191553/article/details/83114935