原题: https://cn.vjudge.net/problem/CodeForces-82D
题意:
n个人排队,每个人有个等待时间。你每次需要从队伍前三中选出两个人pop,加的时间为max,若只剩一个人,加那个人的时间。问sum的最小值。
解析:
发现第 i + 1 i+1 i+1次的三个人为:第 i i i次的前三中留下的一个,后面两个固定。所以用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示后面两个中前面一个为 i i i,之前剩下的那个为 j 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);
}