AtCoder Beginner Contest 286——E - Souvenir

题目链接:

题意:
这里有N座城市,这里还有一些连接不同城市的单程直飞航班。(有向图)
可以城市之间的直飞的航班用长度为N的字符串进行表示。如果从第i个城市到达第j个城市的字符为 Y,表示可以从 i 到 j,否则如果字符是 N,表示不可以从 i 到 j。
每座城市都在卖纪念品,城市i的纪念品价值为Ai,;
思考以下的问题:
我想途径一些直达航班,从城市S出发去往T城市(T和S不同!),沿途中,他会去参观经过的城市,并且购买一个纪念品。
如果从城市S到达城市T有多条路线,我选择的路线决定如下:
1、他尽可能地减少从城市S到达城市T的直达航班数。
2、他试图使他所购买的纪念品价值最大化。
请你确定他是否能够从S–>T,如果可以,请找到满足上述条件的路线的纪念品价值总和。
给你Q个查询序对(U,V)对于每次查询,都请输出对应的最大的纪念品价值。

思路:

本题的性质有两个:

  • 首先是最小化路径长度
  • 其次是最大化经过城市的纪念品的价值总和
    若单看最小化路径长度的话,那么就是一个最短路问题,根据数据范围N<=300可以推出:可用floyd算法求解,因为floyd算法比较好写!且并未超过时间复杂度!
    但是现在需要考虑第二个条件,实际上就是多开一个数组 sum[i, j]表示从城市 i 到达城市 j 的纪念品价值的总和。但要注意状态转移的思路:如果路径严格变小,那么纪念品价值总和也要跟着变化;如果路径长度相等,则可以去纪念品价值总和的一个最大值!注意初始化!

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 3e2 + 10;
int dist[N][N];		//表示从i城市到j城市最短路径 
LL sum[N][N];		//从i城市到j城市的纪念品价值总和
char str[N];		//描述城市之间关系的邻接矩阵
int a[N];			//每座城市的纪念品价值!
 
int main()
{
	int n;
	cin >> n;
	
	for (int i=1; i <= n; i ++)
		cin >> a[i];
	memset (dist, 0x3f, sizeof (dist));	//初始化为无穷大表示不可到达! 
	for (int i=1; i <= n; i ++)
	{
		scanf ("%s", str+1);	//按行输入!
		for (int j=1; j <= n; j ++)
			if (str[j] == 'Y')
				dist[i][j] = 1, sum[i][j] = 1LL*a[i] + 1LL*a[j];
	}
	for (int i=1; i <= n; i ++)
		dist[i][i]=0, sum[i][i] = 1LL*a[i];
	
	for (int k=1; k <= n; k ++)		//枚举从i-->j之间的城市;
		for (int i=1; i <= n; i ++)	//枚举起点!
			for (int j=1; j <= n; j ++)	//枚举终点!
			{
				if (dist[i][j] == dist[i][k] + dist[k][j])
				{
					dist[i][j] = dist[i][k] + dist[k][j];
					sum[i][j] = max(sum[i][j], 1LL*sum[i][k]+1LL*sum[k][j]-1LL*a[k]);
				}
				else if (dist[i][j] > dist[i][k]+dist[k][j])
				{
					dist[i][j] = dist[i][k] + dist[k][j];
					sum[i][j] = sum[i][k] + sum[k][j] - 1LL* a[k];
				}
			} 
	
	int q;	//q个查询!
	cin >> q;
	while (q --)
	{
		int u, v;
		cin >> u >> v;
		
		if (dist[u][v] >= 0x3f3f3f3f) puts("Impossible");
		else cout << dist[u][v] << ' ' << sum[u][v] << endl;	
	} 
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值