UVA10123木板上放石头使木板平衡,递归加强剪枝

52 篇文章 0 订阅
20 篇文章 0 订阅

渣渣表示物理没学好,读完题的第一感觉是题意读错了,又反复读了好几遍,才感觉没读错,

只不过物理没学好,感觉取一个石头是不可能平衡的,然而是可以的,因为有俩个支点,又不是一个,

然后怎样去保持平衡,以左边支点分析,右边支点忽略,如果左边的力矩大于右边的力矩加上1.5乘于木板的

总重量,这里为什么要加这个东西,不理解,后来想了很久,另外咨询了我们班学霸,这里相当于把木板的总重量

抽象为在木板中间,也就是传说的质心,然后力距就是1.5。然后是这个题的做法,这个题爆搜毫无疑问会T,关键在于怎么剪枝,

首先反向思维,放完石头去取相当于没放石头的时候一个一个放石头,然后放的时候,把左边和右边分开,分别按照力矩的大小进行排序,

这里,肯定你放石头的时候先放的是对木板平衡影响比较小的,然后左边不能平衡的时候,在放右边的石头,如此往复,如果左后全部的石头

能够放完,反向输出就是答案了。这里的剪枝很关键,也比较巧妙,不经过分析是

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include<cmath>
#include<climits>
#include<vector>
#include<cfloat>
#include<queue>
#include<cctype>
#include<cstdlib>
#define LL long long
using namespace std;
const int maxn=30;
int L,W,N,li,ri,flag;
struct node
{
    int p,w;
}l[maxn],r[maxn],a[maxn];
int cmp(node a,node b)
{
    return (a.p*a.w)<(b.p*b.w);
}
bool is_balanced(int L,int R)
{
    double wl=0,wr=0;
    for(int i=0;i<R;i++) wr+=(r[i].p+1.5)*r[i].w;
    for(int i=0;i<L;i++) wl+=(l[i].p-1.5)*l[i].w;
    if(wl>wr+1.5*W) return 0;
    wl=0,wr=0;
    for(int i=0;i<R;i++) wr+=(r[i].p-1.5)*r[i].w;
    for(int i=0;i<L;i++) wl+=(l[i].p+1.5)*l[i].w;
    if(wl+1.5*W<wr) return 0;
    return 1;
}
void dfs(int L,int R,int n)
{
    if(n==N)
    {
        for(int i=n-1;i>=0;i--)
            cout<<a[i].p<<' '<<a[i].w<<endl;
        flag=1;
        return;
    }
    for(int i=L;i<li;i++)
    {
        if(is_balanced(i,R)&&!flag)
        {
            a[n].p=-l[i].p;
            a[n].w=l[i].w;
            dfs(i+1,R,n+1);
        }
        else
            break;
    }
    for(int i=R;i<ri;i++)
    {
        if(is_balanced(L,i)&&!flag)
        {
            a[n].p=r[i].p;
            a[n].w=r[i].w;
            dfs(L,i+1,n+1);
        }
        else
            break;
    }
}
void slove()
{
    sort(l,l+li,cmp);
    sort(r,r+ri,cmp);
    flag=0;
    dfs(0,0,0);
    if(!flag) cout<<"Impossible"<<endl;
}
int main()
{
    int t=0;
   while(scanf("%d%d%d",&L,&W,&N)&&L+W+N)
   {
       li=0,ri=0;
       for(int i=0;i<N;i++)
       {
           int p,w;
           scanf("%d%d",&p,&w);
           if(p>0) {r[ri].p=p;r[ri].w=w;ri++;}
           else {l[li].p=-p;l[li].w=w;li++;}
       }
       cout<<"Case "<<++t<<':'<<endl;
       slove();
   }
    return 0;
}

不可能想到这么剪枝的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值