BZOJ4831: [Lydsy1704月賽]序列操作(非常nice的DP& 貪心)
阿新 • • 發佈:2018-11-14
4831: [Lydsy1704月賽]序列操作
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 250 Solved: 93
[Submit][Status][Discuss]
Description
給定一個長度為 n 的非負整數序列 a_1,a_2,...a_n 。你可以使用一種操作:選擇在序列中連續的兩個正整數, 並使它們分別減一。當你不能繼續操作時遊戲結束,而你的得分等於你使用的操作次數。你的任務是計算可能的最小 得分和最大得分。Input
Output
對於每組測試資料 輸出一行兩個非負整數,用一個空格隔開,前者表示可能的最小得分,後者表示可能的最大得分。Sample Input
4
1 2 1 3
5
1 2 1 1 3
Sample Output
2 22 3
思路:求最大值肯定就是直接貪心就好了。
難點在於求最小值,我們用dp[i]表示i前面滿足沒有相鄰的非0值,而且把i取掉的最小代價,開始我想通過相鄰兩項來得到dp公式,發現有後效性,很難dp。
然後我們考慮前面三項,就沒有後效性了。
rep(i,3,N) dp[i]=min(dp[i-2]+a[i],dp[i-3]+max(a[i-1],a[i]));
如上,不難看出是對的。 就是有點難想到,ORZ。
#include<bits/stdc++.h> #definerep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int inf=0x3f3f3f3f; const int maxn=100010; int dp[maxn],a[maxn]; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } int main() { int T,N,ans; scanf("%d",&T); while(T--){ scanf("%d",&N); ans=0; rep(i,1,N) read(a[i]); dp[1]=a[1]; dp[2]=a[2]; rep(i,3,N) dp[i]=min(dp[i-2]+a[i],dp[i-3]+max(a[i-1],a[i])); rep(i,2,N) { int tmp=min(a[i-1],a[i]); a[i]-=tmp; ans+=tmp; } printf("%d %d\n",min(dp[N],dp[N-1]),ans); } return 0; }