2015湖南省赛A题 BZOJ4254 Aerial Tramway 树形dp

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=4254

题意:按照规则选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;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值