题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777
题意:给出n道题目以及每一道题目不同时间做的兴趣值,让你求出所有做题顺序中兴趣值不小于m的比例。按一个分数表示。
分析:首先想到的肯定是深搜,深搜枚举一个全排列,然后同时求和,看和大于等于m有多少种,输出结果,但是n的范围是(0--12)12!不能满足深搜的时间限
制,所以在比赛的时候华丽的超时了。
后面,想到了用dp来做,用dp【i】【j】来表示做了 i 道题目的趣味值为 j 的个数。那么可以用dp【i】【k+a【x】【i】】+=dp【i-1】【k】;但是发现其中的 x 是一个
不好控制的量,x表示从之前没有取过的行里面的所有行,但是怎么表示一个行有没有取过呢。。。是一个难题!!!想到用vector,但是发现它每次还是转移的,那
么我们是不是可以转移呢,但是发现转移后他有可能不只表示一种的,卡到这儿写不下去。
其实是个状态压缩dp,状态压缩就是把一些状态较少的情况用二进制的1表示出现0表示没有出现来表示,这样不仅表示方便快速,转移也方便
前面不能表示的状态我们可以用一个状态压缩dp来表示,///dp[i][j]表示取i的二进制位为1的值兴趣值为j时的个数
那么我们就可以转移了。dp[i+(1<<(j-1))][k+a[tmp+1][j]] += dp[i][k];
代码:
#include <cstdio> #include <cstring> const int N = 13; int dp[1<<13][510]; ///dp[i][j]表示取i的二进制位值兴趣值为j时的个数 int f[N]; ///所有可能情况 int a[N][N]; void isit() { f[1]=1; for(int i=2;i<N;i++) f[i]=f[i-1]*i; } int gcd(int a,int b) { if(b==0) return a; else return gcd(b,a%b); } int main() { int T,m,n; scanf("%d",&T); isit(); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=0;i<=(1<<n);i++) ///二进制能表示的情况 { int tmp=0; ///表示已经表示过的题数 for(int j=1;j<=n;j++) if(i&(1<<(j-1))) tmp++; //printf("%d\n",tmp); for(int j=1;j<=n;j++) { if(i&(1<<(j-1))) continue; for(int k=0;k<=m;k++) { if(k + a[tmp+1][j] >= m) ///把大于值得全部保存到j dp[i+(1<<(j-1))][m] += dp[i][k]; else dp[i+(1<<(j-1))][k+a[tmp+1][j]] += dp[i][k]; } } } if(dp[(1<<n)-1][m] == 0) printf("No solution\n"); else { int tm = gcd(f[n],dp[(1<<n)-1][m]); printf("%d/%d\n",f[n]/tm, dp[(1<<n)-1][m]/tm); } } }
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <string> #include <stack> #include <string.h> using namespace std; int map[20][20]; bool flag[20]; int n,m,ans; void dfs(int p,int sum) { if(p == n) { if(sum >= m) ans ++; return ; } for(int i = 0; i < n; ++ i) { if(flag[i] == 0) { flag[i] = 1; dfs(p+1,sum+map[i][p]); flag[i] = 0; } } } int xxx(int x) { int ans = 1; for(int i = 2; i <= x; ++ i) ans *= i; return ans; } int gcd(int a,int b) { if(a<b) swap(a,b); while(b) { int temp = a%b; a=b; b=temp; } return a; } int main() { int t; scanf("%d",&t); while(t--) { ans = 0; memset(flag,0,sizeof(flag)); scanf("%d %d",&n,&m); for(int i = 0; i < n; ++ i) for(int j = 0; j < n; ++ j) scanf("%d",&map[i][j]); dfs(0,0); if(ans == 0) puts("No solution"); else { int ta=xxx(n); int g=gcd(ta,ans); ta /= g; ans /= g; printf("%d/%d\n",ta,ans); } } return 0; }