LevOJ P1291 西蒙斯的金钱帝国

题目描述

詹姆斯・西蒙斯是世界级的数学家,也是最伟大的对冲基金经理之一。全球收入最高的对冲基金经理,年净赚 15 亿美元。在华尔街,韬光养晦是优秀的对冲基金经理恪守的准则,詹姆斯?西蒙斯也是如此,即使是华尔街专业人士,对他及其旗下的文艺复兴科技公司也所知甚少。然而在数学界,西蒙斯却是大名鼎鼎。早在上个世纪,詹姆斯?西蒙斯就是一位赫赫有名的数学大师。在 2007 年 “华尔街最会赚钱的基金经理” 排行榜中,这位 70 岁的数学大师以年收入 13 亿美元,位列第五。

西蒙斯留下的是关于金钱的问题。想象一下,如果一个国家只有 3 块、6 块或者 10 块三种货币,你就不可能付清 1、2、4、5、7、8、11、14 或者 17 块钱,显然这样的金融系统将会崩溃。给出 N 种货币计算出不能用这 n 种货币表示的最大面值数目(每种货币数量无限),数据保证一定有解。

输入

第一行一个整数 n,表示货币的数目。

接下来 n 行表示 n 种不同的货币。

输出

输出一行,表示不能用这 n 种货币表示的最大面值数目

样例输入

3

3 6 10

样例输出

17

题解

        这题实际上是一种特殊的最短路问题,叫做同余最短路。

        限于我目前对它的理解还不够深刻,暂时还说不出个所以然。具体的讲解可以点击 这里 

也可有参考 OI Wiki 里面的介绍:https://oi-wiki.org/graph/mod-shortest-path/

        下面是此题的AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue> 

using namespace std;

int n, price[100005];	//初始数据 
//u: 起始结点  v:终止结点  w:边权 
int u[100005], v[100005], w[100005], tot;	
//邻接表 
int first[100005], nxt[100005];
//距离 
int dis[100005];

int main(){
	while(scanf("%d", &n) != EOF){
		tot = 0;
		//读取数据 
		for(int i = 0; i < n; ++i){
			scanf("%d", price+i);
			if(price[i] < price[0])	swap(price[i], price[0]);
		}
		//建图 
		int tmp = price[0];
		memset(first, -1, sizeof first);
		for(int i = 0; i < n; ++i){
			for(int j = 0; j < tmp; ++j){
				u[++tot] = j, v[tot] = (j + price[i]) % tmp, w[tot] = price[i];
				nxt[tot] = first[j], first[j] = tot;
			}
		}
		//SPFA求最短路 
		memset(dis, 0x3f, sizeof dis);
		dis[0] = 0;
		bool inq[100005] = {0};
		queue<int> q;
		q.push(0);	inq[0] = 1;
		while(!q.empty()){
			int fq = q.front();
			q.pop(), inq[fq] = 0;
			for(int i = first[fq]; ~i; i = nxt[i]){
				if(dis[v[i]] > dis[u[i]] + w[i]){
					dis[v[i]] =	dis[u[i]] + w[i];
					if(!inq[v[i]])	q.push(v[i]), inq[v[i]] = 1;
				}
			}
		}
		//打印结果 
		int res = -1;
		for(int i = 0; i < tmp; ++i)	res = max(res, dis[i]);
		printf("%d\n", res - price[0]);
	}
	
	return 0;
} 

总结

        不得不说,这题刷新了我对最短路的认知。感觉比较遗憾的是,没能用自己的语言把同余最短路的精髓描述出来,如果后面有机会,应该把它补上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值