【[JLOI2013]卡牌遊戲】
阿新 • • 發佈:2019-01-02
思路太妙了
剛開始yy出了一種比較自然的dp方法,就是按照遊戲的進行來開始dp,設\(dp[i][j]\)表示第\(i\)個人為莊家,還剩下\(j\)個人的概率為多少,但是很快發現這個樣子沒法轉移,因為沒有辦法確定下一個莊家是誰
於是只能將第二維壓成一個狀態\(s\) ,\(dp[i][s]\)表示第\(i\)個人為莊家存活狀態為\(s\)的概率為多少,顯然這個樣子做一個狀壓dp的話我們可以列舉當前的莊家,當前的狀態,以及當前的莊家用的牌是哪一張,之後我們就可以確定下一個莊家是誰,這樣就可以轉移了
但是這個複雜度大概是\(O(nm2^n)\),30%的資料應該還是能過的
正解的思路就相當秒了,我們設\(dp[i][j]\)
初始狀態\(dp[1][1]=1\)
之後我們列舉\(i,j\),之後繼續列舉\(k\)表示莊家(即1)選擇了第\(k\)張牌,由於我們這裡的莊家都是1所以我們要通過模擬第一步進行狀態的轉移
我們的到第一步之後就可以轉化為\(dp[i-1][]\)了
我們枚舉了\(k\)自然就可以得到第一輪被淘汰的人\(p\)
如果\(p=j\),\(j\)上來就被淘汰就不用轉移了
而第 \(p\)個人被淘汰之後,剩下的 \(i-1\) 個人要組成一個新的環,莊家為第 \(p\)個人的下一個。容易算出,當 \(p>j\)
程式碼
#include<iostream> #include<cstring> #include<cstdio> #define re register #define maxn 55 int n,m; int a[maxn]; double dp[maxn][maxn]; inline int read() { char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } int main() { n=read(),m=read(); for(re int i=1;i<=m;i++) a[i]=read(); dp[1][1]=1.0; for(re int i=2;i<=n;i++) { for(re int j=1;j<=i;j++) { for(re int k=1;k<=m;k++) { int p=a[k]%i; if(!p) p=i; if(p>j) dp[i][j]+=dp[i-1][i-p+j]/m; if(p<j) dp[i][j]+=dp[i-1][j-p]/m; } } } for(re int i=1;i<=n;i++) printf("%.2lf",dp[n][i]*100),putchar('%'),putchar(' '); return 0; }