分析:题目要求数学期望,其实就是求最终值
≥M
的概率。暴力的做法:找出所有排列方式,一个一个找:
o(12!)
,贵了。对数据敏感的话,应该比较容易想到状压。
dp[i][msk][j]
代表前
i
个数用了
那么转移方程:
其实第一维根本没什么意义, msk 中 1 的个数就代表了
dp[msk][j]=∑msk第k位为1dp[msk|1<<k][j−p[k][cnt[msk]]]
代码:(由于角标是从 0 <script type="math/tex" id="MathJax-Element-2661">0</script>开始的,所以和上面的有点不太一样)
#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y) for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)
using namespace std;
typedef vector <int> VT;
const int maxn = 1<<12;
const int maxm = 550;
LL dp[maxn][maxm];
int p[12][12],cnt[maxn][13];
int n,m;
LL gcd(LL a,LL b){
return b == 0 ? a : gcd(b,a%b);
}
int main()
{
//freopen("test.in","r",stdin);
FOR(i,0,maxn){
cnt[i][0] = 0;
FOR(j,0,12){
if(i & (1<<j)){
cnt[i][++cnt[i][0]] = j;
}
}
}
int T; scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
FOR(i,0,n)
FOR(j,0,n) scanf("%d",&p[i][j]);
dp[0][0] = 1;
int MSK = 1<<n;
FOR(i,0,m){
FOR(msk,1,MSK){
dp[msk][i] = 0;
FOR(j,1,cnt[msk][0]+1){
int k = cnt[msk][j];
if(i < p[k][cnt[msk][0]-1]) continue;
dp[msk][i] += dp[msk^(1<<k)][i-p[k][cnt[msk][0]-1]];
}
}
}
LL sum = 1;
FOR(i,1,n+1){
sum *= i;
}
LL res = 0;
FOR(i,0,m){
res += dp[MSK-1][i];
}
res = sum - res;
if(!res){
printf("No solution\n");
continue;
}
LL g = gcd(res,sum);
res /= g;
sum /= g;
printf("%lld/%lld\n",sum,res);
}
return 0;
}