Component is missing template or render function
阿新 • • 發佈:2021-12-15
給定一個序列\(a_i\),每次可以刪除前\(3\)箇中的\(2\)個,代價為所刪兩數的\(a_i\)最大值;若數字個數小於\(3\),就一次刪完,代價同樣為\(a_i\)最大值。求刪掉所有數的最小代價以及方案。
\(n\le 1000,1\le a_i\le 10^6\)
Solution
這樣的刪數方式很特別,我們需要尋找一些性質。
從序列的形狀上看,沒有被刪的數應該是 一個單點+一個字尾,也可能只有一個字尾。
啥,你說咋看出來的?字尾顯然,單點多刪幾次就可以發現。
那不就很好dp了嗎。
設\(f_{i,j}\)表示單點位置為\(i\),字尾左端點為\(j\)時,最小代價是多少。
刷表,討論刪掉前\(3\)
\(f_{i,j}\)可以貢獻到:
- \(f_{i,j+2}\)
- \(f_{j+1,j+1}\)
- \(f_{j,j+2}\)
還要考慮只有字尾的情況,不妨設當\(i=j\)時,字尾為從\(i\)開始的區間。
\(f_{i,i}\)可以貢獻到
- \(f_{i+2,i+2}\)
- \(f_{i+1,i+3}\)
- \(f_{i,i+3}\)
然後就是記錄方案。
程式碼:
#include<bits/stdc++.h> #define fi first #define se second #define mp make_pair #define pb push_back using namespace std; inline int read(){ int x=0,f=0; char ch=getchar(); while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar(); while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=getchar(); return f?-x:x; } typedef pair<int,int> pii; const int N=1005; int n,a[N],f[N][N],pre[N][N][2]; void upd(int x,int y,int i,int j,int val){ //用f[i,j]更新f[x,y],val為轉移時的代價 if(f[x][y]>f[i][j]+val){ f[x][y]=f[i][j]+val; pre[x][y][0]=i; pre[x][y][1]=j; } } int main(){ n=read(); for(int i=1;i<=n;++i) a[i]=read(); memset(f,0x3f,sizeof(f)); f[1][1]=0; for(int i=1;i<=n+2;++i){ upd(i+2,i+2,i,i,max(a[i],a[i+1])); upd(i+1,i+3,i,i,max(a[i],a[i+2])); upd(i,i+3,i,i,max(a[i+1],a[i+2])); for(int j=i+1;j<=n+2;++j){ upd(i,j+2,i,j,max(a[j],a[j+1])); upd(j+1,j+1,i,j,max(a[i],a[j])); upd(j,j+2,i,j,max(a[i],a[j+1])); } } vector<pii>ans; int x,y; if(n&1) printf("%d\n",f[n+2][n+2]),x=y=n+2; else printf("%d\n",f[n+1][n+1]),x=y=n+1; while(x>1||y>1){ int px=pre[x][y][0],py=pre[x][y][1]; if(px==py){ if(x==px+2&&y==py+2) ans.pb(mp(px,px+1)); if(x==px+1&&y==py+3) ans.pb(mp(px,px+2)); if(x==px&&y==py+3) ans.pb(mp(px+1,px+2)); }else{ if(x==py+1&&y==py+1) ans.pb(mp(px,py)); if(x==py&&y==py+2) ans.pb(mp(px,py+1)); if(x==px&&y==py+2) ans.pb(mp(py,py+1)); } x=px; y=py; } reverse(ans.begin(),ans.end()); for(int i=0;i<ans.size();++i){ // printf("%d %d\n",ans[i].fi,ans[i].se); if(ans[i].fi<=n) printf("%d ",ans[i].fi); if(ans[i].se<=n) printf("%d ",ans[i].se); printf("\n"); } return 0; } /* 給定一個序列,每次可以刪除前3箇中的2個,代價為max(ai),剩餘一個時代價為a,求最小代價 剩下的元素是一個單點 & 一個字尾 f[i,j]:單點在i,字尾左端點為j,最小時間為多少 f[i,i] -> f[i+2,i+2],f[i+1,i+3],f[i,i+3] f[i,j] -> f[i,j+2],f[j+1,j+1],f[j,j+2] */