2014 Super Training #4 B Problem Arrangement --状压DP
原题:ZOJ 3777 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777
题意:给每个题目安排在每个位置的value。有一个程序随机选择安排的顺序,总的value值大于等于m时,就可以接受这个安排。问能够获得一次满足条件的安排的期望次数。
这题不会做,看网上是用状态压缩DP做的。
定义: dp[i][j]为选取了前i行,趣味和为j的方案种数。
由于程序是每次随机选择一个排列,每次的选择之间不影响,而且每次选中的概率相等,都为dp[S][m]/ fac[n] , 即满足条件的总数/ 总排列数。由于01分布的期望为p , 这里p = dp[S][m]/ fac[n],p为试验一次事件发生的概率,那么,事件一定发生试验的次数为1/p 。 所以结果为 fac[n]/dp[S][m] ,结果用最简形式表示。(S = (1<<n)-1). (参考玻璃年华Alex)
方程:dp[i|(1<<j)][k+a[R][j]] += dp[i][k];
i 即一个01串,其中1的个数表示已经选择的行数,用R表示,则下一次要选第R+1行的数(由于下标从0开始,所以仍是a[R][j]),1的位置表示选取的前i行中选取的数的列的情况,下一次选的时候就不能选这些列了。 然后转移,如果k + a[R][j] > m,则令其为m即可,因为大于m后值就没有意义了。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 14 int fac[N],a[N][N]; int dp[5007][510]; void INIT_FAC() { fac[1] = 1; for(int i=2;i<=12;i++) fac[i] = fac[i-1]*i; } int gcd(int a,int b) { if(!b) return a; return gcd(b,a%b); } int main() { int t,n,m,i,j,k,R; INIT_FAC(); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%d",&a[i][j]); memset(dp,0,sizeof(dp)); dp[0][0] = 1; int S = (1<<n)-1; for(i=0;i<=S;i++) { R = 0; for(j=0;j<n;j++) if(i & (1<<j)) R++; //已访问R行 for(j=0;j<n;j++) //放置第R+1行(下标仍为R),枚举其放置的列 { if(i & (1<<j)) //这列已用过 continue; for(k=0;k<=m;k++) //枚举和 dp[i|(1<<j)][min(k+a[R][j],m)] += dp[i][k]; //和超过m算作m } } if(dp[S][m]) { int g = gcd(fac[n],dp[S][m]); printf("%d/%d\n",fac[n]/g,dp[S][m]/g); } else puts("No solution"); } return 0; }
作者:whatbeg
出处1:http://whatbeg.com/
出处2:http://www.cnblogs.com/whatbeg/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
更多精彩文章抢先看?详见我的独立博客: whatbeg.com