POJ 1062 : 昂贵的聘礼 - 最短路Dijkstra+枚举(难)

 dijkstra处理权值非负情形,最近才开始看最短路。
题目大意:(中文题容易理解)
大致就是说,最终要得到酋长的许诺,每件物品可能有其他物品(1件)能让此物品价格优惠,你可通过交易获得物品从而以最少金钱达到酋长许诺。交易受到“等级限制”。
其中的等级限制处理需要一定的技巧,细节一定要处理好!
输入:(单Case输入)
第一行两个整数M,N(1 <= N <= 100),表示地位等级差距限制和物品(主人)的总数。
接下来依次给出了编号1~N的物品的描述:每个物品的描述——第一行   三个非负整数P、L、X(X < N),依次表示该物品的价格、主人的地位等级和替代品总数。接下来X行   每行包括两个整数T和V,分别表示替代品的编号和"优惠价格"。
输出
得到酋长许诺需要花费的最少金钱。

思路:
(这个题目从开始做的第二天才A的,还是看了其他人博客的题解,且我认为我还不能独立写出它。)
最初看此题时,是在最短路专辑中看到,所以用最短路的思路来处理,但是等级限制方面思考错误——当时以为只要相邻的2个物品主人等级关系符合等级限制就行,其实不然。
正确做法是——所交易的所有物品主人之间等级关系都符合等级限制。   
e.g.,假设酋长等级为5,等级限制为2,那么需要枚举等级从3~5,4~6,5~7
我选择的处理办法是枚举,这个我比较容易看懂,discuss里面还有一些dfs(听说用dfs算水过的…),还有

注意处理几个细节:

1、等级的处理:
题中规定等级超过m的物品不可以交换,包括间接交换。比如a、b、c等级为1、2、3,如果等级限制为1的话,不可以通过a换b,b再换c这种方式,因为a与c的等级超过1。所以如果单次dijkstra求最短路径时,我们很难确定是否加入非集合S的的节点,因为不清楚当前节点是否满足等级要求,或者是否对未来要加入节点等级造成影响。所以目前采用的是枚举范围的方法,有一个事情我们是确定的,即初始节点的等级一定是在等级范围里的。所以我们枚举(lev[s]-m, lev[s])到(lev[s],lev[s]+m)等级范围。在处理最短路径新加入节点时要求节点等级大于等于最低等级,小于等于最高等级。

2、有向图问题:
A物品可以用B物品和C个金币代替,表示A到B的边权为C,但是并不说明B到A也有一条权为C的边,注意!

3、初始化问题:
既然要多次运行dijkstra,那么每个节点的最小路等一定要初始化。我前几次WA就是错在这里,因为每次dijstra数据具有相似性,所以好多数据都检查不出来。

4、表示无穷大的INF    =(1>>30)-1足够,邻接矩阵用int也足够,并不像DISCUSS中有人提到的要开INF=(1>>31)-1.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int M=105,INF=20000000;

bool ok_lim[M],s[M];
int map[M][M],l[M],p[M],d[M];
int lev_lim,n;

void init(){//图的初始化,对角线初始化为0 ——自己到自己花费0
	for(int i=0;i<=n;i++)
		for(int j=0;j<=n;j++)
			if(i==j)
				map[i][j]=0;
			else
				map[i][j]=INF;
}

int dijkstra(){
	int i,j,k,ans=INF;
	memset(s,0,sizeof(s));
	for(i=2;i<=n;i++)
		d[i]=INF;    
	d[1]=0;// for(int i = 1;i <= n;++i)      d[i] = (i == 1 ? 0 : INF);

	for(i=1;i<=n;i++){
		int m=INF;
		k=0;
		for(j=1;j<=n;j++)
			if(!s[j] && d[j]<=m && ok_lim[j]){//在所有未标记且满足等级限制的节点中选出d值最小(最短路径)的
				k=j;
				m=d[j];
			}
		s[k]=1;

		for(j=1;j<=n;j++){//对从x出发的所有边更新d[y]值
			if(ok_lim[j])//满足等级限制时
				d[j] = min ( d[j], d[k]+map[k][j] ) ;
		}
	}

	for(i=1;i<=n;i++){
		d[i]+=p[i];//每个d[i]加上该点花费p[i]再比较
		//cout<<"d[i]--------->"<<d[i]<<endl;

		if(d[i]<ans)
			ans=d[i];
	}
	return ans;
}

int main(){
	int i,j,k,x;
	scanf("%d%d",&lev_lim,&n);
	init();//图初始化

	for(i=1;i<=n;i++){//building map
		scanf("%d%d%d",&p[i],&l[i],&x);
		while(x--){
			scanf("%d",&k);
			scanf("%d",&map[i][k]);
		}
	}
	
	int kinglev=l[1];
	int min_cost=INF,cost;

	for(i=0;i<=lev_lim;i++){
		memset(ok_lim,0,sizeof(ok_lim));//标记数组初始化
		for(j=1;j<=n;j++)
			if(l[j] >= kinglev - lev_lim +i && l[j] <= kinglev+i)
				ok_lim[j]=1;

		cost=dijkstra();

		//cout<<"cost----> "<<cost<<endl;
		if(cost<min_cost)
			min_cost=cost;
	}
	printf("%d\n",min_cost);
	return 0;
}


 
 

注意处理几个细节:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值