『8.25 模擬賽』外賣 (atcoder 100e)
題目鏈接
題目描述
眾所周知,\(cky\)喜歡點外賣。
已知可選的商品有\(n\)種,\(cky\)由於胃容量問題只能點兩份(不能一種點兩份)。\(cky\)要在防止營養過剩的情況下選擇美味度最高的搭配。
具體的,對於每第\(i\)個商品,\(i\)正好是其營養成分,\(s_i\)表示其美味度(商品從\(0\)開始編號)。
對於每種搭配\((a,b)\),其營養程度為(\(a|b\)其中\(|\)表示二進制下的按位或),其美味度為\(s_a+s_b\)。
即\(cky\)要選擇滿足\(a|b\leq k\)中,\(s_a+s_b\)最大的\((a,b)\)。
由於\(cky\)好久沒去體檢,所以不知道能接受多少營養成分,所以希望對於每一種\(k\)
為了送分,\(n\)均可以表示為\(2^N\),其中\(N\)為整數。
解題思路
首先,因為是或運算,所以我們可以想到高維前綴和。
我們分別維護對於每一個k的最大值和次大值,那麽我們就可以利用高維前綴和的方法進行轉移。
我們假設dp[ i ][ 0 / 1 ]表示當前數值i的最大值在原序列中的位置(0),和次大值的位置(1),那麽我們就可以枚舉給i在二進制下的所有為0的位置選擇一個加1,這樣就可以轉移到一個新的狀態tmp,\(tmp=i+(1<<j)\)這樣就可以由i推到tmp,我麽只要比較i和tmp的最大值和次大值,以下有三種情況:
1)tmp的最大值小於i的最大值:那麽我們顯然是要把tmp的次大值變成tmp的最大值,把i的最大值變成tmp的最大值。
2)tmp的次大值小於i的最大值並且tmp的最大值和i的最大值取的不是同一個位置:那麽tmp的次大值變成i的最大值。
2)tmp的次大值小於i的次大值:那麽把tmp的次大值改成i的次大值
代碼
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=(1<<21); int n; int a[maxn],dp[maxn][2]; int main(){ scanf("%d",&n); for(register int i=0;i<(1<<n);i++){ scanf("%d",&a[i]); dp[i][0]=i; } for(register int i=0;i<(1<<n);i++){ for(register int j=0;i+(1<<j)<=(1<<n);j++){ if(!(i&(1<<j))){ int tmp=i|(1<<j); if(a[dp[tmp][0]]<a[dp[i][0]]){ dp[tmp][1]=dp[tmp][0]; dp[tmp][0]=dp[i][0]; } else if(a[dp[tmp][1]]<a[dp[i][0]]&&dp[tmp][0]!=dp[i][0]){ dp[tmp][1]=dp[i][0]; } else if(a[dp[tmp][1]]<a[dp[i][1]]){ dp[tmp][1]=dp[i][1]; } } } } int ans=0; for(register int i=1;i<(1<<n);i++){ ans=max(ans,a[dp[i][0]]+a[dp[i][1]]); printf("%d\n",ans); } }
『8.25 模擬賽』外賣 (atcoder 100e)