1. 程式人生 > >『8.25 模擬賽』外賣 (atcoder 100e)

『8.25 模擬賽』外賣 (atcoder 100e)

spa lin 外賣 tco 答案 iostream 數值 ios gis

題目鏈接

題目描述

眾所周知,\(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)