【BZOJ4254】Aerial Tramway 树形DP
【BZOJ4254】Aerial Tramway
题意:给你一座山上n点的坐标,让你在山里建m条缆车,要求缆车两端的高度必须相等,且中间经过的点的高度都小于缆车的高度。并且不能存在一个点位于至少k条缆车的下方。求缆车的最大总长度。
n,m<=200,k<=10。
题解:这么神奇的题面居然有人能想到要用树形DP。。。
先枚举所有可能的缆车,然后暴力得出这些缆车的关系。因为上面的缆车一定比它下面的缆车长,所以这形成了一个树形结构,我们建树跑树形DP。
用f[x][a][b]表示x的子树中已经减了y个缆车,且一个点最多位于k条缆车下方,的最大总长度。转移时是惯用的树形背包套路,然后用前缀最大值优化一下即可。时间复杂度O(n*m*k)。
#include <cstdio> #include <cstring> #include <iostream> #include <vector> using namespace std; int T,n,m,K,tot,ans,cas; int x[210],y[210],v[210],bel[210],siz[210],g[210][15],f[210][210][15],d[210]; vector<int> ch[210]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } void dfs(int x) { siz[x]=1; int i,j,k,l,y; for(l=0;l<=K;l++) f[x][0][l]=0; for(i=0;i<(int)ch[x].size();i++) { y=ch[x][i],dfs(y); memcpy(g,f[x],sizeof(f[x])); for(j=0;j<=siz[x]&&j<=m;j++) for(k=0;k<=siz[y]&&j+k<=m;k++) for(l=0;l<=K;l++) g[j+k][l]=max(g[j+k][l],f[x][j][l]+f[y][k][l]); siz[x]+=siz[y]; for(j=0;j<=siz[x]&&j<=m;j++) for(l=1;l<=K;l++) f[x][j][l]=max(g[j][l],f[x][j][l-1]); } for(j=min(m,siz[x]);j>=1;j--) for(l=1;l<=K;l++) { f[x][j][l]=max(f[x][j][l],f[x][j-1][l-1]+v[x]); f[x][j][l]=max(f[x][j][l],f[x][j][l-1]); } ans=max(ans,f[x][m][K]); } void work() { tot=0,K--; int i,j; for(i=1;i<=n;i++) x[i]=rd(),y[i]=rd(),ch[i].clear(),bel[i]=0; for(i=1;i<=n;i++) { for(j=i-1;j>=1;j--) { if(y[j]>y[i]) break; if(y[j]==y[i]) { bel[i]=++tot,v[tot]=x[i]-x[j],d[tot]=0; break; } } if(y[j]==y[i]&&bel[i]) for(j++;j<i;j++) if(y[j]<y[i]&&bel[j]&&!d[bel[j]]) d[bel[j]]=1,ch[bel[i]].push_back(bel[j]); } memset(f,0xc0,sizeof(f)); v[++tot]=-1<<20; for(i=1;i<tot;i++) if(!d[i]) ch[tot].push_back(i); ans=-1,dfs(tot); printf("%d\n",ans); } int main() { while(scanf("%d%d%d",&n,&m,&K)!=EOF) { printf("Case %d: ",++cas); work(); } return 0; }//14 3 3 1 8 2 6 3 4 4 6 5 3 6 4 7 1 8 4 9 6 10 4 11 6 12 5 13 6 14 8 14 3 2 1 8 2 6 3 4 4 6 5 3 6 4 7 1 8 4 9 6 10 4 11 6 12 5 13 6 14 8
| 欢迎来原网站坐坐! >原文链接<