1. 程式人生 > >BZOJ4831: [Lydsy1704月賽]序列操作(非常nice的DP& 貪心)

BZOJ4831: [Lydsy1704月賽]序列操作(非常nice的DP& 貪心)

4831: [Lydsy1704月賽]序列操作

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 250  Solved: 93
[Submit][Status][Discuss]

Description

給定一個長度為 n 的非負整數序列 a_1,a_2,...a_n 。你可以使用一種操作:選擇在序列中連續的兩個正整數, 並使它們分別減一。當你不能繼續操作時遊戲結束,而你的得分等於你使用的操作次數。你的任務是計算可能的最小 得分和最大得分。

Input

第一行包含一個正整數 T ,表示有 T 組資料,滿足 T ≤ 200 。 接下來依次給出每組測試資料。對於每組測試資料: 第一行包含一個正整數 n ,滿足 1 ≤ n ≤ 10^5   。 第二行包含 n 個非負整數,表示 a_1,a_2,...a_n ,滿足 Σa_i  ≤ 10^6 。 約 5 組資料滿足 n ≥ 10^3 或 Σa_i  ≥ 10^4 。

Output

對於每組測試資料 輸出一行兩個非負整數,用一個空格隔開,前者表示可能的最小得分,後者表示可能的最大得分。

Sample Input

2
4
1 2 1 3
5
1 2 1 1 3

Sample Output

2 2
2 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>
#define
rep(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; }