poj1062

题目不难,主要难点为:转化为最短路径以及理解dijkstra算法——贪心算法,然后就是变换dijkstra算法求出符合题意的最短路径。

大致题意为:给定n个物品,和限制交易等级。并依次给出每个物品的交易信息:价值、主人等级、以及交易的优惠情况。其中交易的优惠情况为:可交易物品编号、交易成功后剩余原物品价值。现在规定交易过程中直接或间接交易的物品主人等级差不能超过题目给定的限制交易等级M。在该条件限制下问最少需要多少钱可以交易第一件物品。(其实不需要写题意的,本来就是中文)

题意很明确,就是不断的使用较小价值的物品来换取较大价值的物品,当然前提是可以交换。那么此题应该如何入手?首先我们可以这样思考:假设A——B、B——C、C——D可以交易,而且直接和间接的交易等级在控制范围之内。那么交易后的价值为Pab+Pbc+Pcd+Pd,即AB交换的价值+BC交换的价值+CD交换的价值+D物品本身的价值。那么所有从物品1开始的交易路径中最少的交换价值就是最少价钱。而每一条路径又可以看作是路径上边的权值之和+终点物品价值。最少的交换价值即最短路径。那么本题就可以转化为最短路径问题了。

下面再来探讨一下dijkstra算法的核心思想:

上面已经说明了:贪心思想。即首先求出与原点最短距离的点,构成一个集合。然后利用该点对其余点进行扩展。之后继续求出各点到原点的最短路径然后加入到集合中,然后利用该点继续扩展。即贪心准则为:通过已经求得的较小的最段路径来扩展其余点较大的最短路径。由于本题有交易权限限制。故在扩展时不能随意扩展,不能只要相连就扩展。必须满足扩展后该路径上各节点的等级差最大不能超过M,那么扩展时就必须要扩展后限制等级差最大范围,同时在选择最短路径加入已知最短路径集合中时也要控制最大的等级限制。

下面是代码:216K+0MS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Inf 100000100 // 无穷大
#define Max 110 //顶点个数
#define Maxx(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
int trim[Max][Max]; //邻接矩阵
int price[Max]; //物品价值
int pos[Max]; //物品主人等级
int range[Max][2]; //dijkstra算法中各节点的最短路径上等级最小值、最大值
int dis[Max]; //最短路径长度
bool vis[Max];//标记是否以及求出最短路径
int n,m;
int Dijkstra(){ //dijkstra算法求最短路径
	int i,j,k;
	memset(vis,0,sizeof(vis)); //初始化都未并入集合
	for(i=1;i<=n;i++){
		dis[i]=trim[1][i]; //初始化为各点与物品1的距离
		range[i][0]=Min(pos[i],pos[1]); // 初始各物品的最短路径上等级最小值
		range[i][1]=Maxx(pos[i],pos[1]);//初始各物品的最短路径上等级最大值
	}
	vis[1]=true; //将原点标记
	dis[1]=0; //设置最短距离为0
    int maxint,ind;
	for(i=1;i<n;i++){ //循环n-1次,依次求出除开原点外的n-1个点的最短路径
		maxint=Inf; //初始话为无穷大
		ind=1;//初始化为原点,若找不到可并入的最短路径,则以原点代替此次的循环,不影响结果
		for(j=1;j<=n;j++)//求出最短路径
			if(!vis[j] && dis[j]<maxint && range[j][1]-range[j][0]<=m){ //注意控制等级差,若等级差不满足则说明该路径根本就不存在,同时要控制距离dis,只有这两个同时满足才能使一条合法的最短路径
				maxint=dis[j];
			    ind=j;
			}
		vis[ind]=true; //并入已知最短路径点集合
		for(k=1;k<=n;k++) //扩展路径
			if(!vis[k] && dis[ind]+trim[ind][k]<dis[k]){ //注意要控制等级差,只有等级差满足才可以扩展,否则不能延伸。
				int max_range=Maxx(range[ind][1],pos[k]); //求出若可以扩展节点后路径上的最小等级和最大等级
				int min_range=Min(range[ind][0],pos[k]);
				if(max_range-min_range<=m){ //若满足等级差限制
					dis[k]=dis[ind]+trim[ind][k]; // 扩展路径
					range[k][1]=max_range; //重新最短路径上的等级最小值
					range[k][0]=min_range; //重新最短路径上的等级最大值
				}
			}
	}
    int result=Inf; //最少价值,初始化为无穷大
	for(i=1;i<=n;i++)
		if(dis[i]+price[i]<result && range[i][1]-range[i][0]<=m) // 枚举所有最短路径+price值的最小值即为最少价值,注意要加入等级权限限制,防止有未成功求得最短路径的情况
			result=dis[i]+price[i];
	return result;
}
	
int main(){
	scanf("%d%d",&m,&n);
	int num,number,value;
	for(int i=0;i<Max;i++)
		for(int j=0;j<Max;j++)
			trim[i][j]=Inf; //初始化各边为去穷大
	for(int i=1;i<=n;i++){ //输入各物品描述
		scanf("%d%d%d",&price[i],&pos[i],&num); //输入价值、等级、可交换物品个数
		while(num--){ //输入各物品的交换情况
			scanf("%d%d",&number,&value);
			trim[i][number]=value; // 创建有向边
		}
	}
	printf("%d\n",Dijkstra()); //输出结果
	return 0;
}
	
	


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值