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代码:

 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 } 
View Code

 

posted @ 2018-09-03 21:03  ccsu_dj辉  阅读(118)  评论(0编辑  收藏  举报