poj 3411 Paid Roads

这题很特别,因为标记不能简单的为一次,他有可能为多次。。

但又不能不标记,不然无法结束。。

 

大致题意:

有n座城市和m(1<=n,m<=10)条路。现在要从城市1到城市n。有些路是要收费的,从a城市到b城市,如果之前到过c城市,那么只要付P的钱,如果没有去过就付R的钱。求的是最少要花多少钱。

注意:路径是有向的。


这题难点在于“城市与城市之间可能存在多条路径”:

1、  输入数据时可能会出现多条 从城市a到城市b的路径信息,但是费用有所差别;

2、  对于 从城市a到城市b 的同一条路径,允许重复走。

 
先来看一组数据:

6 5

1 2 1 10 10

2 3 4 10 100

2 4 2 15 15

4 1 1 12 12

3 6 6 10 10

如果每条路只允许走一次,那么方案只有1个:

1à2à3à6 共135元

但这组数据的正确答案是67元。为什么?正确的方案如下:

1à2à4à1à2à3à6 共67元

显然1à2重复走了一次,目的是为了先到达城市4,从而使得2à3这段路的费用从100缩减到10元。

       看到这里很多同学好像就恍然大悟,但是问题马上又来了。如果同一条路允许重复走,那么就不能标记了,

但一旦不标记,失去了搜索的限制条件,DFS就无法结束,不是陷入死循环了?

       我刚才说这种思路“对一半,错一半”,“对”是对在“重复走会增加费用”,“错”是错在“重复走的

对象不是某一条路,而是某一个环路”。在同一个环路重复走才会真正增加费用。但是标记环路是很麻烦的,

那么能不能根据某一条路或某一个城市重复走过的次数来判断当前所走的方案已经出现了环路? 答案是可以的。

       上述的例子已经验证过了,同一条路可以重复走,但是不能无限重复走,重复的次数是有限的。那么应该重复

多少次才合理?这与m值有关。题目的m值范围为<=10,那么当人一个城市被到达的次数若  >3次(不包括3),

所走的方案必然出现了环路(网上的同学称之为“闸数”)。

 

       因此只需把bool vist[] 修改为 int vist[] 进行标记,本题就能解决了。


 

 

 

#include"stdio.h"
#include"string.h"
int visit[1000],n,m;
#define inf 99999999
int min;
struct point
{
 int x,y,z,w,v;
}a[1000];
void bfs(int k,int cost)
{
 int i;
 if(k==n&&cost<min)
 {
  min=cost;
  return ;
 }
 for(i=0;i<m;i++)
 {
  if(a[i].x==k&&visit[a[i].y]<3)
  {
   visit[a[i].y]++;
   if(visit[a[i].z]==0)
    bfs(a[i].y,cost+a[i].v);
   else
    bfs(a[i].y,cost+a[i].w);
   visit[a[i].y]--;
  }
 }
}
int main()
{
 int i,j;
 while(scanf("%d%d",&n,&m)!=EOF)
 {
  for(i=0;i<m;i++)
   scanf("%d%d%d%d%d",&a[i].x,&a[i].y,&a[i].z,&a[i].w,&a[i].v);
  memset(visit,0,sizeof(visit));
  min=inf;
  bfs(1,0);
  if(min==inf)
   printf("impossible\n");
  else
   printf("%d\n",min);
 }
 return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值