ZOJ Problem Set - 3777 Problem Arrangement
飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5264
题意:给你n个题目,每个题目放在第i个位置有个值,有个程序可以随机生成1~n的排列,求生成的排列值大于m的期望次数
分析:这里的话需要推一下公式,需要用到极限的定理,因为假设n!种方案里,可行的方案概率为p,不可行为q(即1-p),则期望次数为
limn->无穷p*q^0*1+p*q^1*2+p*q^2*3....+p*q^(n-1)*n这个极限怎么求是关键,这里可以用错位相减法,即将式子乘以q,则为
qlimn->无穷 p*q^1*1+p*q^2*2....+p*q^(n-1)*(n-1)+p*q^(n)*n,然后两式一减,为(1-q)limn->无穷p+p*q^1+p*q^2...p*q^(n-1)-p*q^n*n,等比求和,即limn->无穷p*(1-q^n)/(1-q)-p*q^(n)*n,求极限后为可知这个极限为1/(1-q)即1/p,所以期望的次数是1/p,现在要求的是满足大于等于m的排列有多少个,因为n最大只有12,所以可以用状态压缩,最大也就只有4095,具体怎么搞?假设当前要确定的是第i个位置的数字,然后假设第j个题目放在第i个位置,那么我们可以去找i-1个位置,因为i-1个位置可以用二进制表示,所以可以很快找到然后把i-1个位置里面的值加上j在第i个的值,最后的话,大于等于m的方案数就是我们要求的
复杂度O(n*n*2^n),最后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <map> #include <stack> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=13; int C[maxn]; int ai[maxn][maxn]; int bi[5000][567]; int n,m,T; void init() { scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++)scanf("%d",&ai[i][j]); } int gcd(int a,int b) { if(a<b)swap(a,b); if(a%b==0)return b; return gcd(b,a%b); } void play() { memset(bi,0,sizeof(bi)); bi[0][0]=1; for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { for(int k=1; k<C[n]; k++) { int t=k; bool flag=1; int q=0; for(int l=n-1; l>=0; l--) { if(l+1==j&&t-C[l]<0) { flag=0; break; } if(t-C[l]>=0) { t-=C[l]; q++; } } if(q!=i)flag=0; if(flag) { for(int l=0; l<=500; l++) { int now=l+ai[j][i]; if(now>500)now=500; bi[k][now]+=bi[k-C[j-1]][l]; } } }. } } int fz=0; int fm=1; for(int i=m; i<=500; i++)fz+=bi[C[n]-1][i]; for(int i=1; i<=n; i++)fm*=i; if(fz==0)printf("No solution\n"); else { int p=gcd(fz,fm); printf("%d/%d\n",fm/p,fz/p); } } int main() { C[0]=1; for(int i=1; i<=12; i++)C[i]=C[i-1]*2; scanf("%d",&T); while(T--) { init(); play(); } return 0; }