标签:class cst memset net 压缩 mat string efi 状态
https://cn.vjudge.net/problem/ZOJ-3777
某人出题,有N道,觉得题目难度按题目顺序增加很没意思。他发现将编号为$i$的题目放到$j$号位置能增加$P_{ij}$的趣味值,于是他将题目随机打乱,计算趣味值,如果达不到他想要的趣味值M就将题目重新随机打乱,如此循环。求他打乱次数的期望。
输入N (1 <= N <= 12)、M (1 <= M <= 500).和矩阵Pij (0 <= Pij <= 100),输出一个不可约分的分数。如果永远都达不到,输出“No solution”。
2 3 10 2 4 1 3 2 2 4 5 3 2 6 1 3 2 4
3/1 No solution
如果知道概率,那么$1/$概率就能得到期望
因此就是
\[\frac{1}{\frac{符合条件的情况}{总情况}}\]
总情况是$N!$,可以直接计算,符合条件的情况可以用DP
N<=12,所以可以用状态压缩dp,设$dp[k][F]$为选了k表示的题目,趣味值为F的种类个数,于是可以得到
\[dp[0][0]=1\]
\[dp[k][F]=\sum{dp[k^x][F-P_?]}\]
其中$P_?$是当前问题在这个位置能加的趣味值
由于要计算大于等于M的种类数,于是改变一下,将大于M的都设为M
\[dp[k|x][F+P_?]+=\sum{dp[k][F]}\]
AC代码
#include<cstdio> #include<cstring> #include<cmath> #define REP(r,x,y) for(register int r=(x); r<(y); r++) #define REPE(r,x,y) for(register int r=(x); r<=(y); r++) #define PERE(r,x,y) for(register int r=(x); r>=(y); r--) #ifdef sahdsg #define DBG(...) printf(__VA_ARGS__), fflush(stdout) #else #define DBG(...) (void)0 #endif // sahdsg using namespace std; typedef long long LL; #define MAXN 30000007 int n,m; int p[12][12]; int dp[1<<12][507]; inline int cnt(int i) { i = (i&0x55555555)+((i>>1)&0x55555555); i = (i&0x33333333)+((i>>2)&0x33333333); i = (i&0x0f0f0f0f)+((i>>4)&0x0f0f0f0f); i = (i&0x00ff00ff)+((i>>8)&0x00ff00ff); i = (i&0x0000ffff)+((i>>16)&0x0000ffff); return i; } int f[13]; inline int gcd(int a, int b) { return b==0? a: gcd(b, a%b); } int main() { f[0]=1; REPE(i,1,12) f[i]=f[i-1]*i; int t; scanf("%d", &t); while(0<t--) { scanf("%d%d", &n, &m); REP(i,0,n) { REP(j,0,n) { scanf("%d", &p[i][j]); } } memset(dp,0,sizeof dp); dp[0][0]=1; REP(i,0,(1<<n)-1) { int ones=cnt(i); REPE(j,0,m) if(dp[i][j]) { REP(x,0,n) if(!(i&(1<<x))){ int k=j+p[x][ones]; if(k>m) k=m; dp[i|(1<<x)][k]+=dp[i][j]; } } } int ans=dp[(1<<n)-1][m]; if(!ans) puts("No solution"); else { int ans2=f[n]; int g=gcd(ans,ans2); printf("%d/%d\n", ans2/g, ans/g); } } return 0; }
标签:class cst memset net 压缩 mat string efi 状态
原文地址:https://www.cnblogs.com/sahdsg/p/10927053.html