您的位置:首页 > 其它

cf 11D A Simple Task(状压DP)

2015-03-02 21:12 357 查看
题意:

N个点构成的无向图,M条边描述这个无向图。

问这个无向图中共有多少个环。

(1 ≤ n ≤ 19, 0 ≤ m)

思路:

例子:

4 6

1 2

1 3

1 4

2 3

2 4

3 4

答案:7

画个图发现,直接暴力DFS有太多的重复计算。用DP。

枚举点数(状态),每个状态的起点、终点(起点可以不用枚举,因为反正是一个环,谁作为起点都一样)。

dp[S][i]:状态是S,i是终点 含义:从S中的第一个数s出发到达第i个点的方案数。如果s和i相加,总方案数ans+=dp[S][i]

dp[S][i]=sigma(dp[S'][i'])  S'是去掉第i个点后的点集,i'属于S'且i'和i相连。

*结果除以2的原因:例:1-2-3-4-1 实际上和1-4-3-2-1是一样的。

代码:

int n,m;
char G[25][25];
int S[1<<19]; //全局状态
int cnS;
int P[25]; //全局指针
int cnP;
ll dp[(1<<19)+5][25];
ll ans;

void finds(int nn,int fNum,int nowNum,int nowPos,int Ss){ //长度为nn,共要放fNum个,现在已放nowNum个,现在正在nowPos位置要进行第nowNum+1个放置的尝试
if(nowNum==fNum){
S[++cnS]=Ss;
return;
}
int t1=fNum-nowNum;
rep(i,nowPos,nn-t1+1){
int nSs=Ss+(1<<(i-1));
finds(nn,fNum,nowNum+1,i+1,nSs);
}
}
void calc(int nn,int S){ //总长度为nn,计算状态S哪些位置上为1,存在全局指针P【】中。
cnP=0;
rep(i,1,nn){
int t=(1<<(i-1));
if((S&t)>0){
P[++cnP]=i;
}
}
}
void init(){
cnS=0;
cnP=0;
finds(n,2,0,1,0);
mem(dp,0);

rep(i,1,cnS){
int ss=S[i];
calc(n,ss);
rep(j,2,cnP){
if(G[P[1]][P[j]]==1){
dp[ss][P[j]]=1;
}
}
}
}
void solve(){
rep(i,3,n){ //状态由i个点构成
cnS=0;
cnP=0;
finds(n,i,0,1,0);
rep(tt,1,cnS){
int ss=S[tt];
calc(n,ss);
rep(j,2,cnP){
int pj=P[j]; //终点
int nss=ss-(1<<(pj-1)); //上一个状态
rep(k,2,cnP){ //枚举终点
if(k==j) continue;
if(G[pj][P[k]]==0) continue;
int pk=P[k];
dp[ss][pj]+=dp[nss][pk];
}
if(G[P[1]][pj]==1){
ans+=dp[ss][pj];
}
}
}
}
}

int main(){
cin>>n>>m;

mem(G,0);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
G[a][b]=G[b][a]=1;
}

if(n==1 || n==2){
puts("0");
}
else{
init();
ans=0;
solve();
printf("%I64d\n",ans/2);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: