poj1062-昂贵的聘礼(最短路径问题)

大致题意:

有N个物品,N个物品都有对应的价格,但是在已有某些物品的情况下可以对某些物品减价,求获取第一个物品最低需要花费的金币数。另外每个物品都有对应的等级,在减价的过程中等级差不能超过给定值。

解题思路:

将物品作为结点,减价关系作为边,构成一幅图。可以用Dijkstra算法求得单源最短路径,求出最少需要花费的金币数。注意等级差的问题。设最大允许等级差为order,第一个物品的等级为rank1,可知最短路径上的结点等级需要在[rank1-order, rank1+order]区间内,这样虽然最短路径上其它结点和第一个结点满足等级差条件,但是其它结点之间不一定满足等级差条件。一种解决办法是枚举[rank1-order, rank1]、[rank1-order+1, rank1+1]、……、[rank1, rank1+order]共order+1个区间(等级在这些区间内的结点都满足等级差条件),分别计算满足这些区间的最短路径,从而得到满足等级差条件的最短路径。

C++代码:

//Memory  Time
//312K  79MS

#include <iostream>
using namespace std;

struct node{
	int num;//结点编号
	int weight;//边权值
	node *next;
};

node goods[101];//物品结点

bool fitrank[101];//结点是否满足等级条件

int dis[101];//距离

class prior_queue{
private:
	int a[105];
	int front;
	int rear;
public:
	prior_queue():front(0), rear(0) {}
	void enqueue(int num){
		a[rear] = num;
		rear = (rear+1)%105;
	}
	int extract_min(){
		int min_loc = front;
		for(int i = front; i != rear; i = (i+1)%105){
			if(dis[a[i]] < dis[a[min_loc]]){
				min_loc = i;
			}
		}
		int result = a[min_loc];
		a[min_loc] = a[front];
		front = (front+1)%105;
		return result; //返回具有最小距离的结点编号
	}
	bool is_empty(){
		if(front == rear)
			return true;
		else
			return false;
	}
};

//dijkstra算法,求单源最短路径
void dijkstra(node ns[], int n){
	//初始化距离
	for(int i = 1; i <= n; i++)
		dis[i] = 0x7fffffff;
	dis[1] = 0;
	prior_queue q;
	for(int i = 1; i <= n; i++)
		q.enqueue(ns[i].num);
	while(!q.is_empty()){
		int num = q.extract_min();
		if(fitrank[num] == false || dis[num] == 0x7fffffff)
			continue;
		node* n = &ns[num];
		while(n->next != NULL){
			node* n2 = n->next;
			if(fitrank[n2->num] == false){ //该结点不符合等级差条件
				n = n2;
				continue;
			}
			if(dis[n2->num] > dis[num] + n2->weight){
				dis[n2->num] = dis[num] + n2->weight;
			}
			n = n2;
		}
	}
}
	
int main()
{
	int order, goods_num;
	cin >> order >> goods_num;
	int value[101]; //每个物品的价格
	int rank[101]; //每个物品的地位
	//初始化goods[]
	for(int i = 1; i <= goods_num; i++){
		goods[i].next = NULL;
		goods[i].num = i;
		goods[i].weight = 0;
	}
	for(int i = 1; i <= goods_num; i++){
		int edge_num;
		cin >> value[i] >> rank[i] >> edge_num;
		for(int j = 0; j < edge_num; j++){
			node* n1 = new node;
			cin >> n1->num >> n1->weight;
			n1->next = NULL;
			node *p = &goods[i];
			while(p->next != NULL){
				p = p->next;
			}
			p->next = n1;
		}
	}
	//计算最少的金币数
	int min_distance = 0x7fffffff;
	for(int x = 0; x <= order; x++){
		for(int i = 1; i <= goods_num; i++){
			fitrank[i] = true;
		}
		//标记不符合等级差条件的结点
		for(int i = 2; i <= goods_num; i++){
			if(rank[i] - rank[1] < x - order || rank[i] - rank[1] > x)
				fitrank[i] = false;
		}
		//计算单源最短路径
		dijkstra(goods, goods_num);
		for(int i = 1; i <= goods_num; i++){
			if(dis[i] == 0x7fffffff){
				continue;
			}
			if(min_distance > dis[i] + value[i]){
				min_distance = dis[i] + value[i];
			}
		}
	}
	cout << min_distance << endl;

	//释放动态分配的内存
	for(int i = 1; i <= goods_num; i++){
		node *p = goods[i].next;
		while(p != NULL){
			node *p2 = p;
			p = p->next;
			delete p2;
		}
	}
	return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值