bzoj 4831: [Lydsy2017年4月月赛]序列操作

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

HINT

Source


DP果然还是不太行。。

f[i][j]表示第i个消成j的最大答案,前面一位必定消光而当前位可以有剩

F[i][j]表示第i个消成j的最大答案,当前位必定消光而前一位可以有剩

那么就有f[i][j]=max(f[i-1][a[i]-j],F[i-1][a[i]-j])+a[i]-j;

再令fx为f后缀和

F[i][j]=max(fx[i-1][a[i]-j],F[i-1][a[i]-j])+a[i]-j;

然后最小值设为g,G,与f,F同理

#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int f[2][1000011],F[2][1000011];
int g[2][1000011],G[2][1000011];
int a[1000011]; 
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	int T;
	scanf("%d",&T);
	while(T>0)
	{
		T--;
		int n;
		scanf("%d",&n);
		int i,j;
		int lim=0; 
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			lim=max(lim,a[i]);
		}
		if(n==1)
		{
			printf("0 0\n");
			continue;
		}
		for(j=0;j<=lim;j++)
		{
			f[0][j]=-1000000000;
			F[0][j]=-1000000000;
			g[0][j]=1000000000;
			G[0][j]=1000000000;
			f[1][j]=-1000000000;
			F[1][j]=-1000000000;
			g[1][j]=1000000000;
			G[1][j]=1000000000;
		}
		int p=0,q=1;
		int tmp=min(a[1],a[2]);
    	for(i=0;i<tmp;i++)
	        F[p][a[2]-i]=G[p][a[2]-i]=i;
	    f[p][a[2]-tmp]=tmp;
		g[p][a[2]-tmp]=tmp;
		
		for(i=3;i<=n;i++)
		{
			for(j=1;j<=a[i];j++)
			{
				f[q][j]=max(f[p][a[i]-j],F[p][a[i]-j])+a[i]-j;
				g[q][j]=min(g[p][a[i]-j],G[p][a[i]-j])+a[i]-j;
			}
			for(j=a[i];j<=a[i-1];j++)
			{
				f[q][0]=max(f[q][0],f[p][j]+a[i]);
				g[q][0]=min(g[q][0],g[p][j]+a[i]);
			}
			f[q][0]=max(f[q][0],F[p][a[i]]+a[i]);
			g[q][0]=min(g[q][0],G[p][a[i]]+a[i]);
			for(j=a[i-1]-1;j>=0;j--)
			{
				f[p][j]=max(f[p][j],f[p][j+1]);
				g[p][j]=min(g[p][j],g[p][j+1]);
			}
			for(j=1;j<=a[i];j++)
			{
				F[q][j]=max(f[p][a[i]-j],F[p][a[i]-j])+a[i]-j;
				G[q][j]=min(g[p][a[i]-j],G[p][a[i]-j])+a[i]-j;
			}
			for(j=0;j<=a[i-1];j++)
			{
				f[p][j]=-1000000000;
				F[p][j]=-1000000000; 
				g[p][j]=1000000000;
				G[p][j]=1000000000;
			}
		//	for(j=0;j<=a[i];j++)
		//		printf("%d %d\n",g[q][j],G[q][j]);
		//	printf("\n");
			p=1-p;
			q=1-q;
		}
		int ansmax=-1000000000,ansmin=1000000000;
		for(i=0;i<=a[n];i++)
		{
			ansmax=max(ansmax,f[p][i]);
			ansmin=min(ansmin,g[p][i]);
		}
		printf("%d %d\n",ansmin,ansmax);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值