【题解】poj1116[ACM/ICPC Regional Contest Northeast Europe 2001].Library

题目链接
学习了大佬博客
难度比较大,需要枚举很多种情况,对思维的全面性要求高

#include
#include
#include
#define INF 0x3f3f3f3f
using namespace std;
int xn,yyn,xt,yt,n;
//壁龛的宽度高度、旧书的宽度和高度、货架的数量 
int mindz=INF,minmb=INF;//最少钉子,最少木板 
struct node{
    int x,y,l,x1,x2;
    //左端距离、高度、长度、左端和左支撑钉距离、左端和右支撑点的距离 
    friend bool operator <(node a,node b){
        return a.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(lstelse 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(lstelse if(p[i].x1<=x&&p[i].x2>x&&p[i].x2//起始点钉子在钉子之间且终点在右钉子右边 
        {
            if(x==0)//若起始点为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)//左钉子在起始点与终点之间并且右钉子在终点的右端 
        {
            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//不多想直接拆 
        {
            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].lcontinue;//木板根本放不到钉子上,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(dzint main()
{
    #ifdef local
    freopen("in.txt","r",stdin);
    #endif
    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)solve(i);
        //若第i个书架可以放得下最大的那本书,则开始找其最小耗费 
    }
    printf("%d %d",mindz,minmb);
    return 0;
}

你可能感兴趣的:(ACM/ICPC,poj,枚举,算法艺术与信息学竞赛)