ZOJ-3777 Problem Arrangement(状态压缩DP)
题目链接:https://vjudge.net/problem/ZOJ-3777
题意:
有n个题目,输入n和m,接下来一个n*n的矩阵,a[i][j]表示第i道题放在第j个顺序做可以加a[i][j]的分数,
问做完n道题所得分数大于等于m所需的期望(次数)
题解:
设满足条件的方案数为s,总的方案数位n!,那么期望就是n!/s了。主要任务就是求方案数。
状压DP做法:state是一个n位的二进制数,每一位 1 or 0 代表了该位置是否被占掉了;
dp[k][i][j]-放完前k个,状态为i,获得的分数为j的方案数;
dp[k][i][j]-推出相应的dp[k+1][s][w]即可;
第一维的数组可以不用要,dp[state][k]代表:前i道题的放置情况按state安排,产生分数为k的方案数;
状态state有count个1代表前count个问题已经放好了。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 int dp[1<<13][510],map[13][13]; 6 int m,n; 7 int gcd(long long a,long long b) 8 { 9 if(b!=0) return gcd(b,a%b); 10 return a; 11 } 12 int main() 13 { 14 int T; 15 scanf("%d",&T); 16 while(T--) 17 { 18 scanf("%d%d",&n,&m); 19 for(int i=0;i<n;i++) 20 for(int j=0;j<n;j++) 21 scanf("%d",&map[i][j]); 22 23 memset(dp,0,sizeof(dp)); 24 dp[0][0]=1; 25 for(int i=0;i<(1<<n);i++) 26 { 27 int num=__builtin_popcount(i); //统计该状态1的个数num:代表已经放置完num个问题,准备放置下一个问题 28 for(int j=0;j<n;j++) //枚举可行的放置位置 29 { 30 if(i&(1<<j)) continue; //判断该位置是否已经被放置 31 for(int k=0;k<=m;k++) //转移方程:dp[i][j]表示状态为i时,分数达到j的方案数为多少 32 { 33 if(k+map[num][j]>=m) dp[i|(1<<j)][m]+=dp[i][k]; 34 else 35 dp[i|(1<<j)][k+map[num][j]]+=dp[i][k]; 36 } 37 } 38 } 39 long long sum1=1,sum2=dp[(1<<n)-1][m]; 40 for(int i=1;i<=n;i++) 41 sum1*=i; 42 int ans=gcd(sum1,sum2); 43 if(sum2==0) printf("No solution\n"); 44 else printf("%d/%d\n",sum1/ans,sum2/ans); 45 } 46 }