1. 程式人生 > >2015湖南省賽A題 BZOJ4254 Aerial Tramway 樹形dp

2015湖南省賽A題 BZOJ4254 Aerial Tramway 樹形dp

題意:按照規則選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;
}