名校聯賽DAY.2A層第二題SO(就)題解
問題 B: 就
時間限制: 1 Sec 內存限制: 512 MB題目描述
就so.in/.out
【背景描述】
一排 N 個數, 第 i 個數是 Ai , 你要找出 K 個不相鄰的數, 使得他們的和最大。
請求出這個最大和。
【輸入格式】
第一行兩個整數 N 和 K。
接下來一行 N 個整數, 第 i 個整數表示 Ai 。
【輸出格式】
一行一個整數表示最大和, 請註意答案可能會超過 int 範圍
【樣例輸入】
3 2
4 5 3
【樣例輸出】
7
【數據範圍】
對於 20% 的數據, N, K ≤ 20 。
對於 40% 的數據, N, K ≤ 1000 。
對於 60% 的數據, N, K ≤ 10000 。
對於 100% 的數據, N, K ≤ 100000 , 1 ≤ Ai ≤ 1000000000。
這道題第一反應是DP的舉起你們的雙手,你們盡管打,A了算我輸。
這道題不得不說所有人都以為是DP,但DP再怎麽優化也逃不開對N,K的枚舉,因此只要是DP基本到n^2就已經是極限了,我就打了n^2,果然,60%,雖然不知道有人也是這個復雜度卻過不了的原因,但n^2打的漂亮確乎是60分。因此DP這條路我們不得不放棄。
那麽問題來了,求最優解我們還能打啥呢?DP撲街了,那麽只能是貪心了吧,那麽怎麽貪呢,見一個拿一個很明顯是錯解,那就讓我們回到這個題目對貪心唯一的限制—相鄰。
相鄰就意味著我一旦選了這個點,它周圍的兩個點都不能選了,那麽怎麽去利用這個性質呢?我們大可去把它和它周圍兩點連起來看,為什麽不能在貪這個數的時候把它和其他數建立聯系呢,那麽問題來了,什麽東西可以在極短的時間內找到它的前後兩點(當然不算遍歷),鏈表。
我們大可用鏈表將該點與它左右兩邊還沒被選上(我們暫且這麽說,但從實際意義上來看它是不對的)的點連接起來,只要這個點被選上,就改變鏈表結構。
大家可能看的一頭霧水,好吧,我承認,我語文不好。現在開始講貪心。
首先我們先用優先隊列將每個節點按照權值從大到小排列起來,同時保存每個節點所代表的區間的左右邊界。每次從堆頂取元素時確認是否合法,即他的左右端點之一是否被覆蓋,如果是就繼續pop。
“代表”“覆蓋”是什麽意思呢? 每次我們從堆頂選出來一個最大的位置,然後將其與左右兩個相鄰區間通過鏈表合並,並將這三個位置打上標記,以便刪除,我們個左右兩個區間打上標記並不是因為不再去考慮他們了,而是為了方便接下來的操作,我們要對當前點所維護的信息進行修改,首先是權值,權值改為作區間權值+右區間權值-原權值,這樣當我們將這個節點再次拿出來的時候就可以去代表我們選擇了它兩端的權值了,然後將左右端點更新,鏈表更新即可,由於本題證明雖然容易但難以用語言表述,只能請讀者自己證明了。
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<queue> 8 #include<map> 9 #include<cmath> 10 using namespace std; 11 int m,n; 12 long long a[100005]; 13 map<int,bool> ma[100005]; 14 struct no{ 15 long long data; 16 int bh; 17 bool friend operator < (no a,no b) 18 { 19 if(a.data==b.data) return a.bh>b.bh; 20 return a.data<b.data; 21 } 22 }; 23 priority_queue<no> q1; 24 int pre[100005],fro[100005]; 25 int le[100005],ri[100005]; 26 int main(){ 27 scanf("%d%d",&n,&m); 28 for(int i=1;i<=n;i++) 29 { 30 scanf("%lld",&a[i]); 31 no x; 32 x.data=a[i]; 33 x.bh=i; 34 le[i]=i; 35 ri[i]=i; 36 pre[i]=i-1; 37 fro[i]=i+1; 38 q1.push(x); 39 } 40 long long ans=0; 41 fro[n]=0; 42 a[0]=-1e15; 43 while(m--) 44 { 45 while(!q1.empty()&&ma[le[q1.top().bh]][ri[q1.top().bh]]) 46 q1.pop(); 47 no x=q1.top(); 48 q1.pop(); 49 ans+=x.data; 50 a[x.bh]=-x.data; 51 x.data=-x.data; 52 x.data+=a[pre[x.bh]],a[x.bh]+=a[pre[x.bh]]; 53 x.data+=a[fro[x.bh]],a[x.bh]+=a[fro[x.bh]]; 54 ma[le[pre[x.bh]]][ri[pre[x.bh]]]=ma[le[fro[x.bh]]][ri[fro[x.bh]]]=1; 55 56 le[x.bh]=le[pre[x.bh]]; 57 ri[x.bh]=ri[fro[x.bh]]; 58 if(pre[pre[x.bh]]) 59 fro[pre[pre[x.bh]]]=x.bh; 60 if(fro[fro[x.bh]]) 61 pre[fro[fro[x.bh]]]=x.bh; 62 pre[x.bh]=pre[pre[x.bh]]; 63 fro[x.bh]=fro[fro[x.bh]]; 64 q1.push(x); 65 } 66 printf("%lld\n",ans); 67 return 0; 68 } 69View Code
其實本題set可以更快,有興趣的話可以試一試。
名校聯賽DAY.2A層第二題SO(就)題解