题目链接
大致题意:
输入
n
n
n 和
m
m
m,接下来一个
n
∗
n
n*n
n∗n 的矩阵,
m
p
[
i
]
[
j
]
mp[i][j]
mp[i][j] 表示第
i
i
i 道题放在第
j
j
j 个顺序做可以加
m
p
[
i
]
[
j
]
mp[i][j]
mp[i][j] 的分数,问做完
n
n
n 道题所得分数大于等于
m
m
m 的概率。用分数表示,分母为上述满足题意的方案数,分子是总的方案数,输出最简形式。
解题报告:
状压
d
p
dp
dp,枚举所有 题目被选举的状态。
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示
i
i
i 状态下得分为
j
j
j 的方案总数
代码展示:
#include<bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define mem(a, b) memset(a,b,sizeof(a))
using namespace std;
const int maxn = 1e5 + 5;
const int MOD = 1e9 + 7;
int mp[15][15],n,m;
int dp[(1<<13)+5][505],fac[15];
int main() {
ios::sync_with_stdio(NULL);cin.tie(NULL);cout.tie(NULL);
fac[0]=1;
for(int i=1;i<=12;i++) fac[i]=fac[i-1]*i;
int T;
cin>>T;
while (T--) {
mem(dp,0);
cin>>n>>m;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>mp[i][j];
dp[0][0]=1;
for(int i=0;i<(1<<n);i++){//枚举所有状态
int cnt=0;
for(int j=0;j<n;j++) if((i>>j)&1) cnt++;//该状态下有多少位置已被占有即前cnt道题目已被排列好
for(int j=1;j<=n;j++){
if((i>>(j-1))&1) continue;//被占有跳过
for(int k=0;k<=m;k++){ //状态转移:当前状态可转移至其他哪些状态 例 010 -> 011或110
if(k+mp[cnt+1][j]>=m) dp[i+(1<<(j-1))][m]+=dp[i][k];//第cnt+1道题的放置
else dp[i+(1<<(j-1))][k+mp[cnt+1][j]]+=dp[i][k];
}
}
}
if(dp[(1<<n)-1][m]==0) cout<<"No solution"<<'\n';
else{
int gcd=__gcd(fac[n],dp[(1<<n)-1][m]);
cout<<fac[n]/gcd<<"/"<<dp[(1<<n)-1][m]/gcd<<'\n';
}
}
return 0;
}