[dp][Lydsy1704月賽]序列操作

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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章