POJ2331 Water Pipe 解题报告

来源:互联网 发布:程序员需要学什么语言 编辑:程序博客网 时间:2024/06/08 12:21

题目意思:
你是一个修水管的工人。在一个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 0