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();
}