2015湖南省賽A題 BZOJ4254 Aerial Tramway 樹形dp
阿新 • • 發佈:2019-01-23
題意:按照規則選m條線段,使得不超過k限制,得到權值和最大。
題解:按照給定的規則,一條合法的線段下面必須全部低於此線段,也就是每一個線段一定被某個線段的完全覆蓋,且不會出現線段非重合相交的情況。故所有的線段依據覆蓋關係可形成一個森林,建立虛擬節點0,使得構建一棵樹。然後就是樹形dp,dfs過程中要套一個揹包。其實最重要的是轉換出這個模型。
f[i][d][j] 表示在i節點選取d個線段,重疊最大次數不超過j的最優方案。轉移過程就是對i的兒子揹包,注意要做兩次揹包,一次是不選i這個線段,另一個是必須選i。
程式碼:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int N = 510; const int inf = 1e8; typedef long long ll; int f[N][N][15],val[N]; struct point{ int x,y; }p[N]; int cmp1(point a,point b){ return a.x < b.x; } struct line{ int x1,x2,y; line(){} line(int _x1,int _x2,int _y){ x1 = _x1; x2 = _x2; y = _y; } }le[N]; int cmp2(line a,line b){ return abs(a.x2-a.x1) < abs(b.x2-b.x1); } int n,m,k,num; vector<int> g[N]; vector<int> son[N]; void make_line(){ for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ if(p[i].y==p[j].y){ int flag = 1; for(int d=i+1;d<j;d++) if(p[d].y>=p[i].y) flag=0; if(!flag) continue; le[++num] = line(p[i].x,p[j].x,p[i].y); break; } } } void tro_line(){ for(int i=1;i<=num;i++){ int flag = 1; for(int j=i+1;j<=num;j++)if(le[i].y < le[j].y){ if(le[i].x1>le[j].x1 && le[i].x2<le[j].x2){ flag = 0; g[j].push_back(i); break; } } if(flag){ g[0].push_back(i); } } } int h[N][N],v[N]; int dfs(int x,int pa){ son[x].clear(); int size = 1; int co =0 ; for(int i=0;i<g[x].size();i++)if(g[x][i]!=pa){ int u = g[x][i]; son[x].push_back(u); co++; v[u] = dfs(u,x); size += v[u]; } for(int j=0;j<k;j++){ for(int i=0;i<=m;i++) h[0][i]=-inf; h[0][0] = 0; for(int i=1;i<=co;i++){ int u = son[x][i-1]; for(int d=0;d<=m;d++) h[i][d] = h[i-1][d]; for(int t=0;t<=v[u];t++) for(int d=t;d<=min(size,m);d++) h[i][d] = max(h[i][d] , h[i-1][d-t]+f[u][t][j]); } for(int i=0;i<=m;i++) f[x][i][j] = max(f[x][i][j],h[co][i]); } if(x!=0) for(int j=1;j<k;j++){ for(int i=0;i<=m;i++) h[0][i]=-inf; h[0][1] = val[x]; for(int i=1;i<=co;i++){ int u = son[x][i-1]; for(int d=0;d<=m;d++) h[i][d] = h[i-1][d]; for(int t=0;t<=v[u];t++) for(int d=t+1;d<=min(size,m);d++) h[i][d] = max(h[i][d] , h[i-1][d-t]+f[u][t][j-1]); } for(int i=0;i<=m;i++) f[x][i][j] = max(f[x][i][j],h[co][i]); } for(int i=1;i<=m;i++) f[x][i][0]=-inf; f[x][0][0]=0; for(int i=0;i<=m;i++) for(int j=1;j<k;j++) f[x][i][j] = max(f[x][i][j],f[x][i][j-1]); return size; } int main(){ int cas=0; while(scanf("%d%d%d",&n,&m,&k)!=EOF){ for(int i=1;i<=n;i++){ scanf("%d%d",&p[i].x,&p[i].y); } num=0; sort(p+1,p+1+n,cmp1); make_line(); for(int i=0;i<=num+1;i++) g[i].clear(); sort(le+1,le+1+num,cmp2); tro_line(); val[0]=-inf; for(int i=1;i<=num;i++) val[i] = abs(le[i].x2-le[i].x1); for(int i=0;i<=num;i++)for(int d=0;d<=m;d++)for(int j=0;j<=k;j++)f[i][d][j]=-inf; dfs(0,-1); printf("Case %d: %d\n",++cas,(f[0][m][k-1]>0?f[0][m][k-1]:-1) ); } return 0; }