POJ2331 Water Pipe 解题报告

题目意思:
你是一个修水管的工人。在一个1000*1000的坐标系中需要用水管联通两个城市。显然,为了让你的工作变得简单,你的老板决定水管只能横着或者竖着放。有一些不同型号的水管,会告诉你每一种水管的长度和数量。现在你要做的是求出最少能够接通两个城市的水管数量。
输入:
只有一组数据啦啦~~
首先两个数,代表城市1的横纵坐标;
然后再来两个数,代表城市2的横纵坐标;
然后是一个数n,代表有n种型号的水管;
再之后是n个数,代表每种型号水管的长度;
又是n个数,代表每种型号水管的数量。
输出:
最少需要的水管数量;
要是无解,则输出-1。

PS:这个水管是比较有特色的,因为他们可以交叉、重叠。比如,下面两组水管也是合法的:
第一组:
水管1: 1,1 1,2 1,3
水管2: 1,2 2,2 2,3
第二组:
水管1: 1,1 1,2 1,3
水管2: 1,1 1,2
讲不清楚这个特色,大家自己看看这两组水管吧:)

现在开始讲怎么做~~
首先,我们答案无非分为两种:有答案还是没答案;
显然,我们只需要考虑有答案的:
如果起点和终点是同一个点,那么就不需要你修了(答案直接是0);
否则
假设所有水管的数量和为sum;
那么,我们的答案就是在[1,sum]这个区间里了~

for(ans=1;ans<=sum;ans++)
    if(pd(ans))break;

显然,我们要的只是最少的水管数量。

于是,接下来要用到一种神奇的IDA*算法(具体看代码吧);

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
const int maxn=1010;
using namespace std;
int sx,sy,ex,ey,n,hx[1010],hy[1010],sum,ans;
struct stick{int l,c;}a[20];
void bfs(int *h,int x){
    queue<int>q;//BFS的队列啊~~
    while(!q.empty())q.pop();//以防万一
    h[x]=0;//这个点答案为0(所以memset是-1)
    q.push(x);//显然
    while(!q.empty()){
        x=q.front();
        q.pop();
        for(int i=1;i<=n;i++){//枚举每一根木棍
            //printf("x:%d  lenth:%d\n",x,a[i].l);
            //这个注释是输出当前木棍的信息
            if(x-a[i].l>=1&&h[x-a[i].l]==-1){
                //往左走不能出界并且还没算过
                h[x-a[i].l]=h[x]+1;
                //printf("push : %d\n",x-a[i].l);
                q.push(x-a[i].l);
            }if(x+a[i].l<=1000&&h[x+a[i].l]==-1){
                //往右走
                h[x+a[i].l]=h[x]+1;
                //printf("push : %d\n",x-a[i].l);
                q.push(x+a[i].l);
            }
        }
    }
}
bool pd(stick *a,int x,int d,int k){
//a数组,坐标,deep,kind(是横坐标还是纵坐标)
    stick tmp[20];
    int hv=k?hy[x]:hx[x];//0是横坐标,1是纵坐标
    if(hv==-1||hv+d>ans)return false;
    //要是没算过,显然不符合条件
    if(hv==0){//要是这个答案是0
        if(!k)return pd(a,sy,d,1);
        //讨论纵坐标
        else return true;
    }
    for(int i=1;i<=n;i++)
        tmp[i]=a[i];//由于涉及到修改木棍数量,所以要一个临时数组
    for(int i=1;i<=n;i++){//依次枚举每类木棍
        if(tmp[i].c<=0)continue;//要是没了,下一个
        tmp[i].c--;//那么就先用掉
        if(x-tmp[i].l>=1)//是往左(下)放?
            if(pd(tmp,x-tmp[i].l,d+1,k))return true;
        if(x+tmp[i].l<=1000)//还是往右(上)放?
            if(pd(tmp,x+tmp[i].l,d+1,k))return true;
        tmp[i].c++;//要是左右(上下)都不行,乖乖把木棍放回去吧
    }
    return false;
}
int main(){
    int i,j,k;sum=0;
    scanf("%d%d%d%d%d",&sx,&sy,&ex,&ey,&n);
    //输入起点终点和水管种类数量
    for(i=1;i<=n;i++)
        scanf("%d",&a[i].l);
    //输入水管长度
    for(i=1;i<=n;i++){
        scanf("%d",&a[i].c);
        sum+=a[i].c;
    }
    //每种水管的数量,sum记得累加
    if(sx==ex&&sy==ey){puts("0");return 0;};
    //要是不用走,那直接 return 0; 就好了
    memset(hx,-1,sizeof(hx));
    memset(hy,-1,sizeof(hy));
    bfs(hx,ex);
    bfs(hy,ey);
    //需要这个步骤先算一遍
    for(ans=1;ans<=sum;ans++)
        if(pd(a,sx,0,0))
            break;
    if(ans>sum){puts("-1");return 0;}
    else printf("%d\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值