我方有\(n\)我的,對方有\(m\)我的,每一個人都有一個健康值\(h_i\),有\(d\)次攻擊,每次隨機從全部人中選\(1\)我的,減小其\(1\)健康值。問將對方全部人都消滅的機率是多少?前端
方法\(1\):ios
將全部人的健康值做爲一種狀態,由於\(h\)最大爲\(6\),因此能夠將全部人的健康值狀態經過\(7\)進制數\(hash\)成一個整數,便於保存以及狀態的轉移。假設目前的狀態\(hash\)值爲\(state\),健康值大於\(0\)的人數有\(sz\)個,枚舉全部可能選擇的人,對於其中一我的,使其健康值減\(1\)後的狀態爲\(nextstate\),則有\(dp[nextstate]=dp[state]\times \frac{1}{sz}\)。通過\(d\)次以後全部狀態中,將對方人數爲\(0\)的機率加起來便可。spa
可是有幾個問題:code
方法\(2\):blog
根據健康值分類,統計每種健康值的人數(己方與對方的人分開統計),而後將\(6\)種健康值對應的人數也經過\(7\)進制數\(hash\)到一個整數上,加上對方的人總共最多須要\(12\)位,且高位存儲對方每種健康值的人數,這樣能夠經過判斷\(state\)是否小於等於\(all=(666666)_7\),來肯定對方的人是否全被消滅。排序
而後就是轉移,枚舉健康值\(i\),每次從健康值爲\(i\)的人裏選\(1\)我的,將其健康值減\(1\),選擇健康值爲\(i\)的人的機率爲\(\frac{h[i]}{sum}\),其中\(h[i]\)爲,健康值爲\(i\)的人數,\(sum\)爲總人數。因此有\(dp[state]=dp[nextstate]*\frac{h[i]}{sum}\)。ip
//方法1 #include<iostream> #include<iomanip> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> using namespace std; #define int long long map<int,double>dp; //dp[state]表示從初始狀態轉換到狀態state的機率爲dp[state] vector<int>v; int encode(vector<int>v){ //hash爲13進制 int ans=0,tmp=1; for(auto vv:v){ ans+=(vv+6)*tmp; //增長6的偏移值,保證都爲正數 tmp*=13; } return ans; } vector<int> decode(int state){ //求每一個人的健康值 v.clear(); while(state){ v.push_back(state%13-6); state/=13; } sort(v.begin(),v.end()); //保證有序 return v; } map<int,double> solve(){ map<int,double>mp; for(auto v:dp){ vector<int>state=decode(v.first); for(int i=0;i<state.size();i++){ //枚舉每種選擇,轉移狀態 vector<int>copy(state); if(copy[i]>0) copy[i]--; else if(copy[i]<0) copy[i]++; if(copy[i]==0){ //將健康值爲0的人去除 copy.erase(copy.begin()+i); }else{ sort(copy.begin(),copy.end()); //保證健康值有序 } mp[encode(copy)]+=v.second*1.0/state.size(); } } return mp; } signed main(){ ios::sync_with_stdio(false); cin.tie(0); int n,m,d,sum1=0;cin>>n>>m>>d; for(int i=1;i<=n;i++){ int x;cin>>x;sum1+=x; v.push_back(x); } int sum2=0; for(int i=1;i<=m;i++){ int x;cin>>x;sum2+=x; v.push_back(-x); //將對方的人健康值取負,以在最終的時候區別我方/對方 } if(sum2>d){ //沒法將對方全部人都消滅 cout<<"0"<<endl; return 0; } if(sum1+sum2<=d){ //可將全部人都消滅 cout<<"1"<<endl; return 0; } sort(v.begin(),v.end()); //保證有序 dp[encode(v)]=1; for(int i=0;i<d;i++) dp=solve(); //轉移d次 double ans=0; for(auto v:dp){ vector<int>t=decode(v.first); if(t.size()&&t[0]>0) ans+=v.second; //若是最小的人的健康值都大於0,則對方的人必定都被消滅 } cout<<setiosflags(ios::fixed)<<setprecision(9); cout<<ans<<endl; return 0; }
//方法2 #include<iostream> #include<iomanip> #include<algorithm> #include<cstring> #include<cstdio> #include<map> using namespace std; #define maxn 10 #define INF 0x3f3f3f3f #define int long long int h1[maxn]/*己方健康值爲i的人數*/,h2[maxn]/*對方健康值爲i的人數*/,all=0/*臨界值*/; int encode(){ int ans=0; for(int i=1;i<=6;i++) ans=ans*7+h2[i]; //對方放在高6位 for(int i=1;i<=6;i++) ans=ans*7+h1[i]; //己方放在低6位 return ans; } map<int,double>dp; double dfs(int pos,int state){ if(dp.count(state)) return dp[state]; double &ans=dp[state],sum=0; if(state<=all) return ans=1; //若是高6位全爲0,則對方全被消滅 if(pos==0) return ans=0; for(int i=1;i<=6;i++){ if(!h1[i]) continue; sum+=h1[i]; //總人數 h1[i]--;h1[i-1]++; //當前血量的人減小一個,前一個血量的人增長一個,使得總血量只減小了1 ans+=(h1[i]+1)*dfs(pos-1,encode()); h1[i]++;h1[i-1]--; //注意恢復 } for(int i=1;i<=6;i++){ if(!h2[i]) continue; sum+=h2[i]; h2[i]--;h2[i-1]++; ans+=(h2[i]+1)*dfs(pos-1,encode()); h2[i]++;h2[i-1]--; } ans/=sum; //最後除以總人數 return ans; } signed main(){ ios::sync_with_stdio(false); cin.tie(0); int n,m,d;cin>>n>>m>>d; for(int i=1;i<=n;i++){ int x;cin>>x;h1[x]++; } for(int i=1;i<=m;i++){ int x;cin>>x;h2[x]++; } for(int i=1;i<=6;i++) all=all*7+6; //低6位全滿時的最大值 cout<<setiosflags(ios::fixed)<<setprecision(9); cout<<dfs(d,encode())<<endl; return 0; }