POJ 3835 Columbus's bargain

题目大意:

        哥伦布到达美洲大陆后物资不充足,需要登录补给,因此需要向土著人购买一定的物资,他需要购买N种货物,和土著人交易的规则如下:

                1. 用金币全价购买;

                2. 用金币和一个玻璃珠购买(如果物品价值为k个金币,则可以用k - 1个金币和1个玻璃珠支付);

                3. 用等价的物品交换;

                4. 用一定量金币和一个价钱更低的物品来交换;

        哥伦布发现有一个有趣的现象,就是在4.的情况中,存在这样的交换,比如物品B+k个金币来交换物品A,结果k小于A - B的差价,他把这种交换叫做bargain,因此可以利用这种现象使总支付的金币数量达到最小。

        现有多个测例(测例数给出),每个测例中都会给定物品总数量N(0 < N ≤ 20),以及每种物品的绝对价格(用金币计算)Q和P(Q表示物品编号,P表示对应的金币数,其中0 < Q ≤ N,0 < P ≤ 30),接下来会给定M,表示有M个bargain(0 < M ≤ 20),对于每种bargain对应N1、N2、R,表示物品N2可以用物品N1+R金币换得,其中输入能保证N1价格比N2低并且R小于两者的差价,注意,R可能等于0。

        对于每个测例,按照物品编号从小到大顺序输出每种物品的实际价格(通过各种交换方法取得该物品所支付的最小的金币数量),格式是"n p",n表示物品编号,p表示实际价格(即答案),最后再打印出一共有多少物品的实际价格等于另外两种物品实际价格之和。 

题目链接

注释代码:

/*                                                 
 * Problem ID : POJ 3835 Columbus's bargain
 * Author     : Lirx.t.Una                                                 
 * Language   : C                                
 * Run Time   : 0 ms                                                 
 * Run Memory : 160 KB                                                 
*/ 

#include <memory.h>
#include <stdio.h>

#define	TRUE		1
#define	FALSE		0

//物品价格的无穷大
#define	INF		31

//物品的最大数量
#define	MAXGOODN		21

typedef	int		BOOL;

int		g[MAXGOODN][MAXGOODN];//graph,即图的邻接矩阵
int		dp[MAXGOODN];//dp[i],表示从源点到物品i的当前最短路径

//spfa维护量
int		stk[MAXGOODN];
int		vis[MAXGOODN];

BOOL
relax( int u, int v ) {

	int		nTmp;

	nTmp = dp[u] + g[u][v];
	if ( nTmp < dp[v] ) {
	
		dp[v] = nTmp;
		return TRUE;
	}

	return FALSE;
}
int
main() {

	int		t;//测例数
	int		n, m;//物品数量和bargain数量

	int		i, j, k;//计数变量
	int		no, _no, p;//N1、N2、R
	int		top;//栈顶
	int		tot;//实际价格等于另两个物品实际价格的物品的总数

	int		u, v;//u -> v

	BOOL	fnd;//find,表示是否找到实际价格等于另两个物品实际价格的物品

	scanf("%d", &t);
	while ( t-- ) {
	
		scanf("%d", &n);

		//交易的过程实际上就是从起始经过一次次交换换得最终物品的一条路径
		//路径上的边就是支付的金币数量,物品编号就是图的结点
		//由于起始时手里一件物品都没有,因此源点就表示一种什么都没有(空空如也)的状态而已
		//实际价格就是寻找一条从源点到该物品的一条最短路径

		for ( i = 1; i <= n; i++ )//都初始化为不可达
			for ( j = 1; j <= n; j++ )
				g[i][j] = INF;


        //构造可交换的边

	    //条件2.(包含了条件1.)
		for ( i = 1; i <= n; i++ ) {
		
			scanf("%d%d", &no, &p);
			g[0][no] = p - 1;//开始时,即购买第一件物品用金币+1玻璃珠最划算
		}

		scanf("%d", &m);

		//条件4.
		while ( m-- ) {//将所有bargain的边都连起来

			scanf("%d%d%d", &no, &_no, &p);
			g[no][_no] = p;//表示no + p可以换的_no
		}

		//条件3.
		for ( i = 1; i < n; i++ )
			for ( j = i + 1; j <= n; j++ )
				if ( g[0][i] == g[0][j] ) {//等价货物交换不需要金币
				
					g[i][j] = 0;
					g[j][i] = 0;
				}


        //SPFA
		memset(vis, FALSE, sizeof(vis));
		dp[0]  = 0;
		for ( i = 1; i <= n; i++ ) dp[i] = INF;
		stk[1] = 0;
		top	   = 1;

		while ( top ) {
		
			u      = stk[top--];
			vis[u] = FALSE;

			for ( v = 1; v <= n; v++ )
				if ( g[u][v] != INF && relax( u, v ) && !vis[v] ) {
				
					vis[v]     = TRUE;
					stk[++top] = v;
				}
		}

		for ( i = 1; i <= n; i++ )//输出各个物品的实际价格
			printf("%d %d\n", i, dp[i]);

		//寻找实际价格等于另两个物品实际价格的物品的总数
		tot = 0;
		for ( k = 1; k <= n; k++ ) {

			fnd = FALSE;
			for ( i = 1; i < n; i++ ) {

				for ( j = i + 1; j <= n; j++ )
					if ( k != i && k != j && dp[k] == dp[i] + dp[j] ) {
					
						tot++;
						fnd = TRUE;
						break;//找到后跳出循环,否则会重复计算
					}

				if ( fnd ) break;
			}
		}

		printf("%d\n", tot);
	}

	return 0;
}
无注释代码:

#include <memory.h>
#include <stdio.h>

#define	TRUE		1
#define	FALSE		0

#define	INF		31

#define	MAXGOODN		21

typedef	int		BOOL;

int		g[MAXGOODN][MAXGOODN];
int		dp[MAXGOODN];
int		stk[MAXGOODN];
int		vis[MAXGOODN];

BOOL
relax( int u, int v ) {

	int		nTmp;

	nTmp = dp[u] + g[u][v];
	if ( nTmp < dp[v] ) {
	
		dp[v] = nTmp;
		return TRUE;
	}

	return FALSE;
}
int
main() {

	int		t;
	int		n, m;

	int		i, j, k;
	int		no, _no, p;
	int		top;
	int		tot;

	int		u, v;

	BOOL	fnd;

	scanf("%d", &t);
	while ( t-- ) {
	
		scanf("%d", &n);

		for ( i = 1; i <= n; i++ )
			for ( j = 1; j <= n; j++ )
				g[i][j] = INF;

		for ( i = 1; i <= n; i++ ) {
		
			scanf("%d%d", &no, &p);
			g[0][no] = p - 1;
		}

		scanf("%d", &m);

		while ( m-- ) {

			scanf("%d%d%d", &no, &_no, &p);
			g[no][_no] = p;
		}

		for ( i = 1; i < n; i++ )
			for ( j = i + 1; j <= n; j++ )
				if ( g[0][i] == g[0][j] ) {
				
					g[i][j] = 0;
					g[j][i] = 0;
				}

		memset(vis, FALSE, sizeof(vis));
		dp[0]  = 0;
		for ( i = 1; i <= n; i++ ) dp[i] = INF;
		stk[1] = 0;
		top	   = 1;

		while ( top ) {
		
			u      = stk[top--];
			vis[u] = FALSE;

			for ( v = 1; v <= n; v++ )
				if ( g[u][v] != INF && relax( u, v ) && !vis[v] ) {
				
					vis[v]     = TRUE;
					stk[++top] = v;
				}
		}

		for ( i = 1; i <= n; i++ )
			printf("%d %d\n", i, dp[i]);

		tot = 0;
		for ( k = 1; k <= n; k++ ) {

			fnd = FALSE;
			for ( i = 1; i < n; i++ ) {

				for ( j = i + 1; j <= n; j++ )
					if ( k != i && k != j && dp[k] == dp[i] + dp[j] ) {
					
						tot++;
						fnd = TRUE;
						break;
					}

				if ( fnd ) break;
			}
		}

		printf("%d\n", tot);
	}

	return 0;
}

单词解释:

bead:n, 珠子

savage:n, 野蛮人,土著人

seldom:adv, 很少

continent:n, 洲,大陆

voyage:n, 航行,航程

Frontera:n, 弗隆特拉,人名

Palos:n, 帕罗斯,地名,西班牙南部港口

depart:vi, 启程,出发

Columbus:n, 哥伦布,人名

Christopher:n, 克里斯多夫,人名

August:n, 八月

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值