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