icpc网络赛第二场K Meal
icpc網(wǎng)絡(luò)賽第二場K Meal
題意:
有n個(gè)人,n個(gè)菜,
現(xiàn)在n個(gè)人輪流吃菜,起初S中有n個(gè)菜,第i個(gè)人會(huì)在還沒拿走的菜中隨機(jī)選一個(gè),拿走第j個(gè)菜的概率為ai,j∑k∈Sai,k\frac{a_{i,j}}{\sum_{k∈S}a_{i,k}}∑k∈S?ai,k?ai,j??,然后將這個(gè)菜從集合S中刪除,如果S為空則結(jié)束,否則輪到下一個(gè)繼續(xù)拿菜
讓你輸出第i個(gè)人拿第j個(gè)菜的概率
n<=20 ,ai,ja_{i,j}ai,j?<=100
題解:
比賽時(shí)想好大體思路,好不容易把A寫完,調(diào)了半天,最后沒時(shí)間做K了
題意很好理解,對于不同的拿取方式,對于第i個(gè)人的情況是不同的,不難看出n很小才20,所以我們可以狀壓dp
我們枚舉二進(jìn)制,1表示這個(gè)菜被取了,0表示還未取
對于一個(gè)狀態(tài)state,假設(shè)共cnt個(gè)位置為1,因?yàn)槭琼樞虺圆?#xff0c;所以就是求第cnt個(gè)人的吃菜概率,枚舉其中為1的位置,相當(dāng)于是吃的第j種菜
用f[i]表示當(dāng)前這個(gè)選菜局面的概率,i的二進(jìn)制狀態(tài)下表示菜的選取情況
利用之前的f[]來轉(zhuǎn)移現(xiàn)在的ans[][],用現(xiàn)在的選取狀態(tài)來更新當(dāng)前的f[]
這樣得到轉(zhuǎn)移方程:
這樣復(fù)雜度為O(n*2n),注意因?yàn)閍很小,所以求逆元不要用快速冪,會(huì)超時(shí),直接O(n)預(yù)處理好即可
代碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define int long long #define x first #define y second typedef pair<int,int> pii; const int N=21;int dp[N][1<<N]; int a[N][N]; int f[1<<N]; int ans[N][N]; int sum[N]; int inv[30000000]; const int mod= 998244353; int qmi(int a,int b) {int res=1;while(b){if(b&1) res=res*a%mod;b>>=1;a=a*a%mod;}return res; } void init(){inv[1]=1;for(int i=2;i<=2000;++i)inv[i]=mod-(long long)mod/i*inv[mod%i]%mod; } signed main() {int n;cin >> n;init();for(int i=1;i<=n;i++) {for(int j=0;j<n;j++) cin >> a[i][j],sum[i]=(sum[i]+a[i][j])%mod;}f[0]=1;for(int i=1;i<1<<n;i++){int cnt=0;int tot=0;for(int j=0;j<n;j++)if(i>>j&1) cnt++;for(int j=0;j<n;j++)if(i>>j&1) tot=(tot+a[cnt][j])%mod;for(int j=0;j<n;j++){if(i>>j&1){ // cout<<"sum[cnt]-?tot+a[cnt][j]="<<sum[cnt]-tot+a[cnt][j]<<endl;ans[cnt][j]=(ans[cnt][j]+f[i^(1ll<<j)]*a[cnt][j]%mod*inv[sum[cnt]-tot+a[cnt][j]])%mod;f[i]=(f[i]+f[i^(1ll<<j)]*a[cnt][j]%mod*inv[sum[cnt]-tot+a[cnt][j]])%mod;}} // cout<<f[i]<<endl;}for(int i=1;i<=n;i++){for(int j=0;j<n;j++)if(j==0)cout<<ans[i][j];else cout<<" "<<ans[i][j];cout<<endl;} }總結(jié)
以上是生活随笔為你收集整理的icpc网络赛第二场K Meal的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: The 2019 ICPC Asia S
- 下一篇: url转发怎么提升排名()