HLG 1259 HaHa's Morning【状态压缩DP】
题意: HAHA 早上有 n 件事情要做, 知道了有 m 个关系,每个关系 a ,b 表示a 事情必须在b 事情之前做,问做完这些事共有多少种可能的顺序。
分析: 可以把题目看成是 一个有向图,其中共有多少种拓扑排序的方法。
首先图中不能有环,可以先传递闭包,看看有没有 g[i][i]=1 的情况出现,如果有则说明有环。
由于 n <=17 ,1<<17 相对较小,可以枚举所有状态进行递推。
① 递归写法
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) int g[18][18]; long long dp[1<<18]; int n; long long c_c(int s) { long long res=dp[s]; if(res>=0) return res; int i,j; for(i=0;i<n;i++) // 判断是否有冲突的情况 if(s&(1<<i)) for(j=0;j<n;j++) if(g[j][i]&&!(s&(1<<j))) return dp[s]=0; dp[s]=0; for(j=s;j;j^=i) { i=j&-j; dp[s]+=c_c(s^i); } return dp[s]; } int main() { int flag,m,a,b,i,j,k,st,stat; while(scanf("%d%d",&n,&m)!=EOF) { flag=0; clr(g); while(m--) { scanf("%d%d",&a,&b); g[a-1][b-1]=1; } for(k=0;k<n;k++) for(i=0;i<n;i++) if(g[i][k]) for(j=0;j<n;j++) g[i][j]=g[i][j]||(g[i][k]&&g[k][j]); // 是否有环 for(i=0;i<n;i++) if(g[i][i]) break; if(i<n) { printf("0\n"); continue; } st=(1<<n)-1; // 总状态数 memset(dp,0xff,sizeof(dp)); dp[0]=1; printf("%lld\n",c_c(st)); } return 0; }