【枚举】poj_1116 Library 【黑书】

Library

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 961 Accepted: 323

Description

Castaway Robinson Crusoe is living alone on a remote island. One day a ship carrying a royal library has wrecked nearby. Usually Robinson brings any useful stuff from the shipwreck to his island, and this time he has brought a big chest with books.

Robinson has decided to build a bookcase for these books to create his own library. He cut a rectangular niche in the rock for that purpose, hammered in wooden pegs, and placed wooden planks on every pair of pegs that have the same height, so that all planks are situated horizontally and suit to act as shelves.

Unfortunately, Robinson has discovered that one especially old and big tome does not fit in his bookcase. He measured the height and width of this tome and has decided to redesign his bookcase in such a way, as to completely fit the tome on one of the shelves, taking into account locations of other shelves and the dimensions of the niche. With each shelf in the bookcase, one of the following operations should be made:


1. Leave the shelf on its original place.

2. Move the shelf to the left or to the right.

3. Shorten the shelf by cutting off a part of the plank and optionally move it to the left or to the right.

4. Move one of the pegs to a different place at the same height and move the shelf to the left or to the right.

5. Shorten the shelf by cutting off a part of the plank, move one of the pegs to a different place at the same height, and optionally move the shortened shelf to the left or to the right.

6. Remove the shelf from the bookcase along with both supporting pegs.

We say that the shelf is properly supported by its pegs, if exactly two distinct pegs support the shelf and the center of the shelf is between its pegs or coincides with one of the pegs. The original design of Robinson's library has all the shelves properly supported by their pegs and lengths of all shelves are integer number of inches. The Robinson may only cut an integer number of inches from the planks, because he has no tools for more precise measurements. All remaining shelves after the redesign must be properly supported by their pegs.

You are to find the way to redesign Robinson's library to fit the special old tome without changing original design too much. You have to minimize the number of pegs that are to be removed from their original places during the redesign (operations 4 and 5 remove one peg, and operation 6 removes two pegs). If there are different ways to solve the problem, then you are to find the one that minimizes the total length of planks that are to be cut off (operations 3 and 5 involve cutting something from the planks, and operation 6 counts as if cutting off the whole plank). Width of planks and diameter of pegs shall be considered zero.

The tome may not be rotated. The tome should completely (to all its width) stand on one of the shelves and may only touch other shelves, their pegs or niche's edge.

Input

The first line of the input file contains four integer numbers XN, YN, XT, and YT, separated by spaces. They are, correspondingly, width and height of the niche, and width and height of the old tome in inches (1 <= XN, YN, XT, YT <= 1000).

The second line of the input file contains a single integer number N (1 <= N <= 100) that represents the number of the shelves. Then N lines follow. Each line represents a single shelf along with its two supporting pegs, and contains five integer numbers yi, xi, li, x1i, x2i, separated by spaces, where:

?yi (0 < yi < YN) - the height of the ith shelf above the bottom of the niche in inches.
?xi (0 <= xi < XN) - the distance between the left end of the ith shelf and the left edge of the niche in inches.
?li (0 < li <= XN - xi) - the length of the ith shelf in inches.
?x1i (0 <= x1i <= li/2) - the distance between the left end of the ith shelf and its leftmost supporting peg in inches.
?x2i (li/2 <= x2i <= li; x1i < x2i) - the distance between the left end of the ith shelf and its rightmost supporting peg in inches.

All shelves are situated on different heights and are properly supported by their pegs. The problem is guaranteed to have a solution for the input data.

Output

The output file shall contain two integer numbers separated by a space. The first one is the minimal number of pegs that are to be removed by Robinson from their original locations to place the tome. The second one is the minimal total length of planks in inches that are to be cut off during the redesign that removes the least number of pegs.

Sample Input

11 8 4 6
4
1 1 7 1 4
4 3 7 1 6
7 2 6 3 4
2 0 3 0 3

Sample Output

1 3

Source

Northeastern Europe 2001

 本题的正解是枚举,要求是一最小代价将书放在书架上,对于为数不多的书架进行枚举,将书放在每个书架上的情况列举出来,然后对于其上方可以挡住这本书的书架进行最优化的处理:尽量不拆木板和钉子,不行尽量少拆木板,再不行移动钉子,还是不行就只能拆掉整个书架了。做题过程中要仔细考虑木板是否稳定的问题,移动时要注意木板重心要在两个支撑的钉子中间,代码中基本上在判断条件中出现的*2或/2都是跟重心有关,不理解的读者建议自行画图思考,具体的内容见代码及详细注释
--------------------- 
作者:ljyfearless 
原文:https://blog.csdn.net/ljyfearless/article/details/78593671 
 

【总结】:

这个题目原本以为比较麻烦,自己也试着在草稿纸上写过每个情况,居然发现,这个题目,异常多细节,

真的非常多细节,后来看到以上博主的代码,我真的醍醐灌顶,怪不得这是区域赛的一道题,

太考验一个人的细心和代码的实践能力了。


【题解】:

题意:就是在题目所给的木板上所有的位置,让你放进一本古书,

这本书有厚度和高度,所以你放的时候必须竖着放,而且不能有木板挡着它。

然后如果有挡着书的木板可以有6种操作:

1、不动

2、左右移动,重心必须在两个栓子之间。

3、通过锯一段木板。

4、通过拔掉栓子,左右移动

5、移动栓子    和     锯木板 同时实现。

6、把栓子和木板都撤了。

 

注意:撤木板也相当于锯。

 

问:请问这本古书放在哪一个位置 使得栓子移动最小,如果栓子移动是相同时,要锯木板最小。

 

后来我看了一下黑书的解释,这个解释就是枚举所有的位置,


【思路】:

第一步,预处理所有的木板和高度,两个栓子的位置,以x来表示,最后排序一下。

第二步,依次从下往上枚举木板所在的高度,然后通过这块木板合法的左右移动,或者移动栓子来枚举这一高度所有的x位置

第三步,在第二步的基础上,看看有没有上面的木板挡着这本古书,如果有,则用最小的花费来使得这个古书能放到这个位置。

 

不断循环 第二步和第三步,然后在此过程上来更新答案。


【分析】:

思路异常清晰,我以为自己有能力写的时候,发现真的困难重重。

所以,没有办法的办法就是看懂别人的代码了。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int xn,yyn,xt,yt,n,mindz=0x3f3f3f3f,minmb=0x3f3f3f3f;
struct node{
    int x,y,l,x1,x2;
    friend bool operator <(node a,node b){
        return a.y<b.y;//重载小于号,用来排序
    }
}p[105];
void move(int k,int x,int &dz,int &mb){
    for(int i=k+1;i<=n;i++){
//木板是有序的,故在枚举完在第k个木板上放书后,要判断k以上的木板是否会阻挡书的放置 
        if(p[i].y>=p[k].y+yt)break;//以上的都挨不到书了;
        if(p[i].x+p[i].l<=x||p[i].x>x+xt)continue;//此木板不会对书架造成影响;
        if(p[i].x2<=x){//此木板右钉子在起始点左端;
            int lst;//注意,可以通过左移来避免对书架造成影响
            if(x<=2*p[i].x1)//要考虑重心的问题
                lst=2*(x-p[i].x1);
            else
                lst=x;
            if(lst<p[i].l)mb+=p[i].l-lst;
        }
        else if(p[i].x1>=x+xt){//木板左钉子在终点的右端 
            int lst;//与上种情况同理 
            if(p[i].x2-x-xt<=xn-p[i].x2)
                lst=2*(p[i].x2-x-xt);
            else lst=xn-x-xt;
            if(lst<p[i].l)mb+=p[i].l-lst;
        }
        else if(p[i].x1<=x&&p[i].x2>x&&p[i].x2<x+xt){//起始点钉子在钉子之间且终点在右钉子右边 
            if(x==0){//若起始点为零,只能删掉一整条边 
                dz+=2;
                mb+=p[i].l;
            }
            else{//移钉 
                //注意:对于此情况,是将木板靠到最左边,最右的部分截掉; 
                dz++;
                if(p[i].l>x)mb+=p[i].l-x;//截木板;
            }
        }
        else if(p[i].x1>x&&p[i].x1<x+xt&&p[i].x2>=x+xt){//左钉子在起始点与终点之间并且右钉子在终点的右端 
            if(x+xt==xn){//若终点到最右端了,只能删掉一整条边 
                dz+=2;
                mb+=p[i].l;
            }
            else{//移钉 
                //注意:对于此情况,是将木板靠到最右边,最左的部分截掉; 
                dz++;
                if(p[i].l>xn-x-xt)mb+=p[i].l-xn+x+xt;//截木板;
            }
        }
        else if(p[i].x1<=x&&p[i].x2>=x+xt){//两钉子在起点终点的两端之外 
            if(x==0&&xt==xn)//只能全拆掉
                dz+=2;
            else
                dz++;
            int lst=max(x,xn-x-xt);
            if(p[i].l>lst)
                mb+=p[i].l-lst;
        }
        else if(p[i].x1>x&&p[i].x2<x+xt){//不多想直接拆 
            dz+=2;
            mb+=p[i].l;
        }
    }
}
void solve(int k){
    for(int i=0;i<=xn-xt;i++){//枚举书的起始位置 
        int dz=0,mb=0;
        if(i+p[k].l<p[k].x1)continue;// 木板根本放不到钉子上,continue;
        if(2*(p[k].x1-i)>p[k].l||//重心出界(左钉子)
            2*(i+xt-p[k].x2)>p[k].l||//重心出界(右钉子) 
            p[k].x2-i>p[k].l||//木板长度不够 (左端点到右钉子距离超过了木板长度) 
            i+xt-p[k].x1>p[k].l)//木板长度不够 (右端点到左钉子距离超过了木板长度) 
            dz++;//需要移动钉子,操作++;
        move(k,i,dz,mb);
        if(dz<mindz||(dz==mindz&&mb<minmb)){
            mindz=dz;
            minmb=mb;
        }
    }
}
int main(){
    scanf("%d%d%d%d",&xn,&yyn,&xt,&yt);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d%d",&p[i].y,&p[i].x,&p[i].l,&p[i].x1,&p[i].x2);
        p[i].x1+=p[i].x;
        p[i].x2+=p[i].x;
    }
    sort(p+1,p+n+1);//按照书架的高度从低到高排序
    for(int i=1;i<=n;i++){
        if(p[i].y+yt>yyn)
            break;
//若从第i个书架的高度加上书的高度大于最大高度,书无法放在i以后的任何一个书架上,直接break;
        if(p[i].l>=xt)//若第i个书架可以放得下最大的那本书,则开始找其最小耗费 
            solve(i);
    }
    printf("%d %d",mindz,minmb);
    return 0;
}

1、预处理:

    scanf("%d%d%d%d",&xn,&yyn,&xt,&yt);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d%d",&p[i].y,&p[i].x,&p[i].l,&p[i].x1,&p[i].x2);
        p[i].x1+=p[i].x;
        p[i].x2+=p[i].x;
    }

按题目的格式输入,然后进行预处理。

预处理步骤是,处理两个栓子的位置。把左下角看作原点。然后每一个栓子用横坐标x来表示成x1,x2。

    sort(p+1,p+n+1);//按照书架的高度从低到高排序

 进行排序,目的是从下到上进行枚举,因为这本古书在壁橱里,如果枚举到上面可能会出现放在书架上,会顶着天花板。

    for(int i=1;i<=n;i++){
        if(p[i].y+yt>yyn)
            break;
//若从第i个书架的高度加上书的高度大于最大高度,书无法放在i以后的任何一个书架上,直接break;
        if(p[i].l>=xt)//若第i个书架可以放得下最大的那本书,则开始找其最小耗费 
            solve(i);
    }

 然后,这个书架也必须要 比 古书的宽度要长。然后进行枚举  这一书架高度上的所有可以放的位置。


 

2、更新答案:

void solve(int k){
    for(int i=0;i<=xn-xt;i++){
            //枚举书的起始位置 
        int dz=0,mb=0;
        if(i+p[k].l<p[k].x1)continue;// 木板根本放不到钉子上,continue;
        if(2*(p[k].x1-i)>p[k].l||//重心出界(左钉子)
            2*(i+xt-p[k].x2)>p[k].l||//重心出界(右钉子) 
            p[k].x2-i>p[k].l||//木板长度不够 (左端点到右钉子距离超过了木板长度) 
            i+xt-p[k].x1>p[k].l)//木板长度不够 (右端点到左钉子距离超过了木板长度) 
            dz++;//需要移动钉子,操作++;
        move(k,i,dz,mb);
        if(dz<mindz||(dz==mindz&&mb<minmb)){
            mindz=dz;
            minmb=mb;
        }
    }
}

 3、计算在该位置时的最小花费 移动栓子 和 锯 木板 最小长度。

void move(int k,int x,int &dz,int &mb){
    for(int i=k+1;i<=n;i++){
//木板是有序的,故在枚举完在第k个木板上放书后,要判断k以上的木板是否会阻挡书的放置 
        if(p[i].y>=p[k].y+yt)break;//以上的都挨不到书了;
        if(p[i].x+p[i].l<=x||p[i].x>x+xt)continue;//此木板不会对书架造成影响;
        if(p[i].x2<=x){//此木板右钉子在起始点左端;
            int lst;//注意,可以通过左移来避免对书架造成影响
            if(x<=2*p[i].x1)//要考虑重心的问题
                lst=2*(x-p[i].x1);
            else
                lst=x;
            if(lst<p[i].l)mb+=p[i].l-lst;
        }
        else if(p[i].x1>=x+xt){
//木板左钉子在终点的右端 
            int lst;//与上种情况同理 
            if(p[i].x2-x-xt<=xn-p[i].x2)
                lst=2*(p[i].x2-x-xt);
            else lst=xn-x-xt;
            if(lst<p[i].l)mb+=p[i].l-lst;
        }
        else if(p[i].x1<=x&&p[i].x2>x&&p[i].x2<x+xt){
//起始点钉子在钉子之间且终点在右钉子右边 
            if(x==0){//若起始点为零,只能删掉一整条边 
                dz+=2;
                mb+=p[i].l;
            }
            else{//移钉 
                //注意:对于此情况,是将木板靠到最左边,最右的部分截掉; 
                dz++;
                if(p[i].l>x)mb+=p[i].l-x;//截木板;
            }
        }
        else if(p[i].x1>x&&p[i].x1<x+xt&&p[i].x2>=x+xt){
//左钉子在起始点与终点之间并且右钉子在终点的右端 
            if(x+xt==xn){//若终点到最右端了,只能删掉一整条边 
                dz+=2;
                mb+=p[i].l;
            }
            else{//移钉 
                //注意:对于此情况,是将木板靠到最右边,最左的部分截掉; 
                dz++;
                if(p[i].l>xn-x-xt)mb+=p[i].l-xn+x+xt;//截木板;
            }
        }
        else if(p[i].x1<=x&&p[i].x2>=x+xt){//两钉子在起点终点的两端之外 
            if(x==0&&xt==xn)//只能全拆掉
                dz+=2;
            else
                dz++;
            int lst=max(x,xn-x-xt);
            if(p[i].l>lst)
                mb+=p[i].l-lst;
        }
        else if(p[i].x1>x&&p[i].x2<x+xt){//不多想直接拆 
            dz+=2;
            mb+=p[i].l;
        }
    }
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值