Two out of Three(dp 队伍前3选2)

原题: https://cn.vjudge.net/problem/CodeForces-82D

题意:

n个人排队,每个人有个等待时间。你每次需要从队伍前三中选出两个人pop,加的时间为max,若只剩一个人,加那个人的时间。问sum的最小值。

解析:

发现第 i + 1 i+1 次的三个人为:第 i i 次的前三中留下的一个,后面两个固定。所以用 d p [ i ] [ j ] dp[i][j] 表示后面两个中前面一个为 i i ,之前剩下的那个为 j j 的答案。

转移需要考虑情况。一般情况下,枚举前3个中剩下的那个,往后转移。边界再特判一下即可。

这里需要维护一个路径,也是比较简单的。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define repp(i,a,b) for(register int i=a;i>=b;i--)
#define mmm(p) memset(p,0,sizeof p)
#define pill pair<int,int>
#define debug(i) printf("#%d\n",i)
typedef long long LL;
int read(){ int ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
const int maxn=1009,maxm=1e4+8;

int a[maxn];
int dp[maxn][maxn];
pill pre[maxn][maxn];//前一个状态
pill op[maxn][maxn];//操作

void out(int i,int j){
    if(i==2&&j==1)return;
    int _i,_j;
    _i=pre[i][j].first;
    _j=pre[i][j].second;
    if(_j>_i)swap(_i,_j);
    out(_i,_j);
    if(op[i][j].second==-1){
        printf("%d\n",op[i][j].first);
    }
    else{
        int _1=min(op[i][j].second,op[i][j].first);
        int _2=max(op[i][j].second,op[i][j].first);
        printf("%d %d\n",_1,_2);
    }
}

int main(){
    memset(dp,0x3f,sizeof(dp));
    int n=read();
    rep(i,1,n){
        a[i]=read();
    }
    dp[2][1]=0;
    rep(i,2,n){
        rep(j,1,i-1){
            if(i<n-1){
                if(dp[i][j]+max(a[j],a[i])<dp[i+2][i+1]){
                    dp[i+2][i+1]=dp[i][j]+max(a[j],a[i]);
                    pre[i+2][i+1]={i,j};
                    op[i+2][i+1]={j,i};
                }
                if(dp[i][j]+max(a[j],a[i+1])<dp[i+2][i]){
                    dp[i+2][i]=dp[i][j]+max(a[j],a[i+1]);
                    pre[i+2][i]={i,j};
                    op[i+2][i]={j,i+1};
                }
                if(dp[i][j]+max(a[i+1],a[i])<dp[i+2][j]){
                    dp[i+2][j]=dp[i][j]+max(a[i+1],a[i]);
                    pre[i+2][j]={i,j};
                    op[i+2][j]={i+1,i};
                }
            }
            else if(i==n-1){
                if(dp[i][j]+max(a[i],a[j])<dp[n+1][n]){
                    dp[n+1][n]=dp[i][j]+max(a[i],a[j]);
                    pre[n+1][n]={i,j};
                    op[n+1][n]={i,j};
                }
                if(dp[i][j]+max(a[n],a[j])<dp[n+1][i]){
                    dp[n+1][i]=dp[i][j]+max(a[n],a[j]);
                    pre[n+1][i]={i,j};
                    op[n+1][i]={n,j};
                }
                if(dp[i][j]+max(a[i],a[n])<dp[n+1][j]){
                    dp[n+1][j]=dp[i][j]+max(a[i],a[n]);
                    pre[n+1][j]={i,j};
                    op[n+1][j]={i,n};
                }
            }
            else if(i==n){
                if(dp[i][j]+max(a[n],a[j])<dp[n+1][n+1]){
                    dp[n+1][n+1]=dp[i][j]+max(a[n],a[j]);
                    pre[n+1][n+1]={i,j};
                    op[n+1][n+1]={n,j};
                }
            }
        }
    }
    rep(j,1,n){
        if(dp[n+1][j]+a[j]<dp[n+1][n+1]){
            dp[n+1][n+1]=dp[n+1][j]+a[j];
            pre[n+1][n+1]={n+1,j};
            op[n+1][n+1]={j,-1};
        }
    }
    int ans=dp[n+1][n+1];
    printf("%d\n",ans);
    out(n+1,n+1);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/88651230