4831: [Lydsy1704月赛]序列操作
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 207 Solved: 70
[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
HINT
Source
鸣谢Tangjz提供试题
sol:
显然是个dp题了。
f[i][j][0/1],表示做到第i个数,他的值为j,前面的数是否存在时的最小操作数
考虑从f[i]转移到i+1
当j<=a[i+1]的时候,同时减去j
f[i][j][0/1]->f[i+1][a[i+1]-j][0]
当j>a[i+1]的时候,同时减去a[i+1]
f[i][j][0]->f[i+1][0][1]
当i-1为0时,枚举k<=min(j-1,a[i+1])
f[i][j][0]->f[i+1][a[i+1]-k][1]
容易发现当k从大到小枚举的时候,只要算f[i][j][0]的后缀最小值即可。
最大值类似处理即可
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
typedef double db;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int inf=707406378;
const int N=1e6+7;
int f[N][2],g[N][2],h[N][2],a[N],Max;
inline void solve()
{
n=read();
if(n==1)
{
n=read();
printf("0 0\n");
return;
}
Max=0;
for(int i=1;i<=n;++i) a[i]=read(),Max=max(Max,a[i]);
// memset(f,127/3,sizeof(f));
for(int i=0;i<=Max;++i) f[i][0]=f[i][1]=inf;
f[a[1]][0]=0;
for(int i=1;i<n;++i)
{
for(int j=0;j<=a[i];++j) h[j][0]=f[j][0],h[j][1]=f[j][1];
for(int j=0;j<=a[i];++j) f[j][0]=f[j][1]=inf;
for(int j=0;j<=a[i];++j)
{
if(j<=a[i+1]) f[a[i+1]-j][0]=min(f[a[i+1]-j][0],min(h[j][0],h[j][1])+j);
else f[0][1]=min(f[0][1],h[j][0]+a[i+1]);
}
int tmp=1e9,t=min(a[i]-1,a[i+1]);
for(int j=t+1;j<=a[i];++j) tmp=min(tmp,h[j][0]);
for(int k=min(a[i]-1,a[i+1]);k>=0;--k)
{
f[a[i+1]-k][1]=min(f[a[i+1]-k][1],tmp+k);
tmp=min(tmp,h[k][0]);
}
}
int tmp=min(f[0][1],f[0][0]);
for(int i=1;i<=a[n];++i)
tmp=min(tmp,f[i][0]);
printf("%d ",tmp);
for(int i=0;i<=Max;++i) f[i][0]=f[i][1]=0;
// memset(f,0,sizeof(f));
f[a[1]][0]=1;
for(int i=1;i<n;++i)
{
for(int j=0;j<=a[i];++j) h[j][0]=f[j][0],h[j][1]=f[j][1];
for(int j=0;j<=a[i];++j) f[j][0]=f[j][1]=0;
for(int j=0;j<=a[i];++j)
if(h[j][0]||h[j][1])
{
if(j<=a[i+1]) f[a[i+1]-j][0]=max(f[a[i+1]-j][0],max(h[j][0],h[j][1])+j);
else f[0][1]=max(f[0][1],h[j][0]+a[i+1]);
}
int tmp=0,t=min(a[i]-1,a[i+1]);
for(int j=t+1;j<=a[i];++j) tmp=max(tmp,h[j][0]);
for(int k=min(a[i]-1,a[i+1]);k>=0;--k)
{
if(tmp) f[a[i+1]-k][1]=max(f[a[i+1]-k][1],tmp+k);
tmp=max(tmp,h[k][0]);
}
}
tmp=0;
if(f[0][1]!=inf) tmp=max(tmp,f[0][1]);
if(f[0][0]!=inf) tmp=max(tmp,f[0][0]);
for(int i=1;i<=a[n];++i)
if(f[i][0]!=inf)
tmp=max(tmp,f[i][0]);
printf("%d\n",tmp-1);
}
int main()
{
// freopen("4831.in","r",stdin);
// freopen("4831.out","w",stdout);
int T=read();
while(T--) solve();
}