No Tipping UVA - 10123 DFS搜索剪枝

问题

https://vjudge.net/problem/UVA-10123

分析

一开始没看懂题目,不知道这种两个支点的怎么计算。
参考:https://blog.csdn.net/HelloWorld10086/article/details/38641299
优化的代码:https://www.cnblogs.com/staginner/archive/2011/12/21/2295362.html
所有的距离都乘以2,消除小数部分。
将拿下的部分倒过来看,看作是一个一个往上面放置的过程。
贪心:首先放中间的,然后再放两边的,两边的排序,按照力矩从小到大尝试往上放,左右两边轮流来。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int maxn=20+5;
const double eps=1e-6;
struct Pack{
    int pos,w;
    double ll,lr,rr,rl;
    Pack():pos(0),w(0),ll(0),lr(0),rr(0),rl(0) {}
};
bool cmp1(const Pack &lhs,const Pack &rhs){
    return lhs.ll<rhs.ll;
}

bool cmp2(const Pack &lhs,const Pack &rhs){
    return lhs.rr<rhs.rr;
}

vector<Pack> Left,Mid,Right,rec;
int n,s,kase=0;
double ll,lr,rr,rl,L,W;

bool DFS(int d,int ld,int rd,double lw,double rw){
    if(d==n && lw<eps && rw<eps){
        return true;
    }
    if(ld<Left.size() && lw+Left[ld].ll<eps){
        rec.push_back(Left[ld]);
        if(DFS(d+1,ld+1,rd,lw+Left[ld].ll,rw-Left[ld].lr)) return true;
        rec.pop_back();
    }
    if(rd<Right.size() && rw+Right[rd].rr<eps){
        rec.push_back(Right[rd]);
        if(DFS(d+1,ld,rd+1,lw-Right[rd].rl,rw+Right[rd].rr)) return true;
        rec.pop_back();
    }
    return false;
}

int main(void){
    while(scanf("%lf%lf%d",&L,&W,&n)==3 && L){
        Left.clear();
        Mid.clear();
        Right.clear();
        rec.clear();
        ll=lr=rr=rl=0;
        L=2*L;
        rl=lr=(L/2+3)*((3+L/2)/L*W)/2;
        ll=rr=(L/2-3)*((L/2-3)/L*W)/2;
        for(int i=0;i<n;++i){
            Pack temp;
            scanf("%d%d",&temp.pos,&temp.w);
            temp.pos<<=1;
            if(temp.pos<=3 && temp.pos>=-3){
                rl+=(temp.pos+3.0)*temp.w;
                lr+=(3.0-temp.pos)*temp.w;
                Mid.push_back(temp);
                rec.push_back(temp);
            } else if(temp.pos<-3){
                temp.ll=(-3-temp.pos)*temp.w;
                temp.lr=(3-temp.pos)*temp.w;
                Left.push_back(temp);
            } else{
                temp.rr=(temp.pos-3)*temp.w;
                temp.rl=(temp.pos+3)*temp.w;
                Right.push_back(temp);
            }
        }
        sort(Left.begin(),Left.end(),cmp1);
        sort(Right.begin(),Right.end(),cmp2);
        printf("Case %d:\n",++kase);
        if(!DFS(Mid.size(),0,0,ll-rl,rr-lr)) printf("Impossible\n");
        else {
            for(int i=rec.size()-1;i>=0;--i){
                printf("%d %d\n",rec[i].pos>>1,rec[i].w);
            }
        }
    }
    return 0;
}

DP 状态压缩+记忆化搜索

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int maxn=20+5;
const double eps=1e-6;
struct Pack{
    int pos,w;
    double ll,lr,rr,rl;
    Pack():pos(0),w(0),ll(0),lr(0),rr(0),rl(0) {}
};
bool cmp1(const Pack &lhs,const Pack &rhs){
    return lhs.ll<rhs.ll;
}

bool cmp2(const Pack &lhs,const Pack &rhs){
    return lhs.rr<rhs.rr;
}

vector<Pack> pack,rec;
int n,kase=0,dp[1<<20],s;
double ll,lr,rr,rl,L,W;

int DP(int cur,double lw,double rw){
    int &temp=dp[cur];
    if(temp!=-1) return temp;
    if(cur==s-1 && lw<eps && rw<eps){
        return temp=1;
    }
    for(int i=0;i<n;++i){
        if(cur&(1<<i)) continue;
        double ld=pack[i].ll-pack[i].rl,rd=pack[i].rr-pack[i].lr;
        if(lw+ld<eps && rw+rd<eps && DP(cur^(1<<i),lw+ld,rw+rd)==1){
            rec.push_back(pack[i]);
            return temp=1;
        }
    }
    return temp=0;
}

int main(void){
    while(scanf("%lf%lf%d",&L,&W,&n)==3 && L){
        pack.clear();
        rec.clear();
        ll=lr=rr=rl=0;
        L=2*L;
        rl=lr=(L/2+3)*((3+L/2)/L*W)/2;
        ll=rr=(L/2-3)*((L/2-3)/L*W)/2;
        for(int i=0;i<n;++i){
            Pack temp;
            scanf("%d%d",&temp.pos,&temp.w);
            temp.pos<<=1;
            if(temp.pos<=3 && temp.pos>=-3){
                temp.rl=(temp.pos+3.0)*temp.w;
                temp.lr=(3.0-temp.pos)*temp.w;
            } else if(temp.pos<-3){
                temp.ll=(-3-temp.pos)*temp.w;
                temp.lr=(3-temp.pos)*temp.w;
            } else{
                temp.rr=(temp.pos-3)*temp.w;
                temp.rl=(temp.pos+3)*temp.w;
            }
            pack.push_back(temp);
        }
        printf("Case %d:\n",++kase);
        s=1<<n;
        //-1代表未访问,0代表该状态非法,1代表改状态合法
        for(int i=0;i<s;++i) dp[i]=-1;
        DP(0,ll-rl,rr-lr);
        if(dp[(1<<n)-1]!=1) printf("Impossible\n");
        else {
            for(int i=0;i<rec.size();++i){
                printf("%d %d\n",rec[i].pos>>1,rec[i].w);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值