题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777
题目大意:有n道题,每道题被安排在不同的位置有不同的值,求安排结果>=m在所有结果中的比例
题目思路:很明显的状压DP,但是>=m需要转换。由于m范围很小,所以可以想到为它多开一维,dp[i][j],i表示状态,j表示值。一个数字的二进制表示选题状态,从1~1^n-1正好可以全部表示,而且后面的一定可以由前面的转换而来。转换也很简单,对于i中每一位进行遍历,遇到1就说明这位有,说明可以从没有这位的情况转过来,而这一位可以作为现在才加入这个状态,也就是i中1的数量就是他被放入的位置,这样就可以得到现在放入这道题会得到多少价值,然后从0~m所有价值+他的价值进行转移,如果>=m就压到m即可。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
using namespace std;
const int MAXN = 1e4+10;
int n,m,a[20][20],dp[MAXN][505];
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
rep(i,1,n){
rep(j,1,n){
scanf("%d",&a[i][j]);
}
}
int endd=(1<<n)-1;
dp[0][0]=1;
rep(i,1,endd){
int tnt=0;
rep(j,1,n){
int temp=1<<(j-1);
if(temp&i){
tnt++;
}
}
rep(j,1,n){
int temp=1<<(j-1);
if(temp&i){
rep(k,0,m){
int pp=min(m,k+a[j][tnt]);
dp[i][pp]+=dp[i-temp][k];
}
}
}
}
if(dp[endd][m]==0){
printf("No solution\n");
}
else{
int aint=1;
rep(i,2,n)aint*=i;
int x=gcd(dp[endd][m],aint);
printf("%d/%d\n",aint/x,dp[endd][m]/x);
}
}
return 0;
}