BZOJ4831: [Lydsy1704月赛]序列操作(非常nice的DP& 贪心)
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
第一行包含一个正整数 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
4
1 2 1 3
5
1 2 1 1 3
Sample Output
2 2
2 3
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; }
It is your time to fight!