CSU 1776/BZOJ 4254 Aerial Tramway(树形dp+topsort)

来源:互联网 发布:淘宝怎么开启返利管理 编辑:程序博客网 时间:2024/06/03 21:06

1776: Aerial Tramway

      Time Limit: 1 Sec     Memory Limit: 128 Mb     Submitted: 59     Solved: 28    


Description

An aerial tramway, cable car, ropeway or aerial tram is a type of aerial lift which uses one or two stationary ropes for support while a third moving rope provides propulsion. With this form of lift, the grip of an aerial tramway cabin is fixed onto the propulsion rope and cannot be decoupled from it during operations. -- Wikipedia 

You own a park located on a mountain, which can be described as a sequence of n points (xi, yi) from left to right, where xi,yi>0, xi<xi+1, yi!=yi+1 (that means there will not be horizontal segments in the mountain skyline), illustrated below(the numbers are the corresponding x-coordinate): 

Since the mountain is very sloppy, some aerial tramways across the park would be very helpful. In the figure above, people can go from p4 to p9 directly, by taking a tram. Otherwise he must follow a rather zigzag path: p4-p5-p6-p7-p8-p9. 
Your job is to design an aerial tramway system. There should be exactly m trams, each following a horizontal segment in the air, between two points pi and pj. "Horizontal" means yi=yj, “in the air" means all the points in between are strictly below, i.e. yk<yi for every i<k<j. For example, no tram can travel between p2 and p9, because p4 is not strictly below p2-p9. However, you can have two trams, one from p2 to p4, and one p4 to p9. There is another important restriction: no point can be strictly below k or more tramways, because it’ll be dangerous. For example, if k=3, we cannot build these 3 tramways simultaneously: p1-p14, p4-p9, p6-p8, because p7 would be dangerous. 
You want to make this system as useful as possible, so you would like to maximize the total length of all tramways. For example, if m=3, k=3, the best design for the figure above is p1-p14, p2-p4 and p4-p9, the total length is 20. If m=3, k=2, you have to replace p1-p14 with p11-p13, the total length becomes 9.

Input

There will be at most 200 test cases. Each case begins with three integers n, m and k(1<=n,m<=200, 2<=k<=10), the number of points, the number of trams in your design and the dangerous parameter introduced earlier. The next line contains n pairs of positive integers xi and yi.(1<=xi,yi<=105).

Output

For each test case, print the case number and the maximal sum. If it is impossible to have exactly m tramways, print -1.

Sample Input

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 47 1 8 4 9 6 10 4 11 6 12 5 13 6 14 8 

Sample Output

Case 1: 20 Case 2: 9 

Hint

Source

湖南省第十一届大学生计算机程序设计竞赛



        给你n个点,然后每个点有横纵坐标,如果两个点高度相同那么可以建立缆车连接线,但是前提是两点连线之间的点高度必须严格低于他们的高度。给你建立m条缆车路线的任务,问你最多能够使得缆车线路总长度为多少。但是要注意,一定是严格建立m条路线,不能多也不能少,如果建不了那么多则输出-1。最后还有一个限制,任何一个点它上面能够有的缆车路线条数严格小于K。
        首先这题很容易往dp方面去想,但是说实话想到树形dp还是不简单的。我们把每一个可以建立的缆车路线想象为一个背包,价值为它的长度,就变成了让你取m个物品,使得总价值最大的一个01背包问题。但是由于有一个K的限制,所以必须用上树形dp。
        用上树形dp,我们就可以加一维表示到当前点的缆车最大深度。我们把路线的相互包含关系求出来,然后用topsort建立一棵树(设置一个虚拟总根,价值为0),在这棵树上进行dp。我们考虑dp[x][i][j]表示在以x为根的子树中取i条路线,然后最大层叠深度小于等于j的最大长度,那么有三个转移方程:dp[x][i][j]=max(dp[x][i][j],dp[x][i-k][j]+dp[y][k][j]),其中y为x的儿子,这条意思是先不考虑x节点,然后在x的所有儿子中做01背包;做完了这个之后再有dp[x][i+1][j+1]=max(dp[x][i+1][j+1],dp[x][i][j]+w[x]),表示再把当前点也考虑进去;注意到,我定义dp数组的时候,第三维是最大层叠深度小于等于j,注意这个深度是可以小于j的,所以说我们最后还要处理这个,把每一个深度都的值更新为小于等于他的最大值,有dp[x][i][j]=max(dp[x][i][j],dp[x][i][j-1])。
        然后,在转移的过程中,要注意由于此题要求取的缆车路线严格等于m,所以我们在初始的时候就要把所有的值赋值为-1,然后在转移过程中,所有的转移一定不能从值为-1的点转移过来。然后初始化的时候是把dp[x][0][i]赋值为0,这个好理解。最后是复杂度,枚举节点是n,枚举取的路线数量是m,深度是K,然后还有枚举儿子,看起来复杂度好像可以到四次方,但是按照树形dp常用的优化方法,每次做背包的时候个数只枚举到当前已经dfs到的数量,可以把复杂度降下来,最后是O(KNM)。
        然后具体的还要处理好线段的包含关系以及top sort。具体见代码:
#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<cstring>#include<vector>#include<queue>#define LL long long#define N 210using namespace std;int w[N],size[N],dp[N][N][11];int n,m,K,sz,x[N],y[N],d[N];vector<int> g[N],gra[N];struct seg{int b,e,h;};vector<seg> segment;bool inclusive(int x,int y)//判断两个路线是否是包含关系{    if (segment[x].b>segment[y].b) return 0;    if (segment[x].e<segment[y].b) return 0;    if (segment[x].h<=segment[y].h) return 0;    return 1;}void topsort()//top sort建树{    queue<int> q;    for(int i=1;i<=sz;i++)        if (!d[i])        {            q.push(i);            g[0].push_back(i);//0为虚拟的总根        }    while(!q.empty())    {        int x=q.front(); q.pop();        for(int i=0;i<gra[x].size();i++)        {            int y=gra[x][i];            if (--d[y]==0)            {                q.push(y);                g[x].push_back(y);//建边            }        }    }}void dfs(int x){    size[x]=1;    for(int i=0;i<=K;i++) dp[x][0][i]=0;    for(int i=0;i<g[x].size();i++)    {        int y=g[x][i];        dfs(y); size[x]+=size[y];        for(int j=min(m,size[x]);j>=0;j--)//每次只枚举到当前size            for(int k=0;k<=K;k++)                for(int t=0;t<=size[y]&&t<=j;t++)//儿子也只枚举到当前size                    if (dp[x][j-t][k]>=0&&dp[y][t][k]>=0)//所有转移都不能从-1来                        dp[x][j][k]=max(dp[x][j][k],dp[y][t][k]+dp[x][j-t][k]);    }    for(int i=min(m,size[x])-1;i>=0;i--)        for(int j=0;j<K;j++)            if (dp[x][i][j]>=0)//把当前点x也考虑进去                dp[x][i+1][j+1]=max(dp[x][i+1][j+1],dp[x][i][j]+w[x]);    for(int i=1;i<=K;i++)        for(int j=0;j<=size[x]&&j<=m;j++)//处理第三维的小于等于            dp[x][j][i]=max(dp[x][j][i],dp[x][j][i-1]);}void init(){    segment.clear();    memset(d,0,sizeof(d));    memset(g,0,sizeof(g));    memset(gra,0,sizeof(gra));    memset(dp,-1,sizeof(dp));    memset(size,0,sizeof(size));}int main(){    int T_T=0;    while(~scanf("%d%d%d",&n,&m,&K))    {        init(); K--;        for(int i=1;i<=n;i++)            scanf("%d%d",&x[i],&y[i]);        for(int i=1;i<=n;i++)            for(int j=i+1;j<=n;j++)            {                if (y[j]>y[i]) break;                if (y[i]==y[j])                {                    segment.push_back(seg{x[i],x[j],y[i]});                    w[segment.size()]=x[j]-x[i]; break;                }            }        sz=segment.size();        for(int i=0;i<sz;i++)            for(int j=i+1;j<sz;j++)                if (inclusive(i,j))                {                    d[j+1]++;                    gra[i+1].push_back(j+1);                }        topsort(); dfs(0);        if (sz<m) printf("Case %d: -1\n",++T_T);//注意一定要特判路线数与m的关系,因为我虚拟了一个总根,所以当        else printf("Case %d: %d\n",++T_T,dp[0][m][K]);//sz比m小1的时候,dp会自动把总根算进去,然后输出一个正数而不是-1    }    return 0;}

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 办信用卡没有家庭地址的怎么办 钱付了货没收到怎么办 在苏宁易购上买东西地址错了怎么办 手机分期付款银行卡丢了怎么办 华硕笔记本鼠标不动了怎么办 韵达快递不派送怎么办 中通快递不派送怎么办 农业银行信用卡密码输错三次怎么办 农业银行卡多次输错密码怎么办 想把店长弄走怎么办 济南银座卡过期了怎么办 银座购物卡丢失后怎么办 银座的卡丢了怎么办 银行卡换了旧卡怎么办 大理市民卡丢了怎么办 市民卡内的钱怎么办 宝付支付乱扣款怎么办 苏宁任性贷逾期怎么办 第二次跟家里开口要钱还网贷怎么办 网贷到家来要钱怎么办 网贷贷不了啦急要钱怎么办 百度推广竞价关键词太长怎么办 药店位置差客流少怎么办 网站上的用词违规怎么办 苹果16g内存不够怎么办 手机16g内存不够怎么办 在私企年纪大了怎么办 谷歌浏览器显示不安全打不开怎么办 4s密码多次错误怎么办 苹果4s手机系统错误怎么办 汽车充电口坏了怎么办 如果手机充不了电怎么办 淘宝买的家电坏了怎么办 衣服皱了没有熨斗怎么办 油烟机油盒坏了怎么办 实体店不给换货怎么办 台式电脑鼠标不动了怎么办 电脑开机一直长鸣报警怎么办 国外电话卡网速太慢怎么办 滴滴提现忘记登录密码怎么办 微信提现支付密码忘记了怎么办