Lightoj 1316 A Wedding Party

A Wedding Party

We all know that we have a big and exciting wedding party ahead. So we made a plan to go to buy a gift for the wedding party. We all gathered at a place and we were just about to buy the gift. Unfortunately, we find that we have a ‘Team Practice Contest’ ahead. Now before going to the contest we have to buy the gift. As time is too short we will try to buy the gift on the way to the contest. We will try to visit as many shops as possible. The city map is represented by a graph with N nodes and M edges. N nodes represent the N junctions and M edges represent the M unidirectional roads connecting the cities. Every road has a cost which represents the required time to use the road. The contest is running at junction N-1 and we will start our journey at junction 0. And there are exactly S shops located at different junctions.

Now given the location of the shops you have to find the route from junction 0 to junction N-1 which will visit maximum number of shops with minimum time (first maximize the number of shops then minimize the time to visit them). We can visit a junction more than once.

Input
Input starts with an integer T (≤ 50), denoting the number of test cases.

Each case begins with three non negative integers N (2 ≤ N ≤ 500), M (1 ≤ M ≤ 10000) S (0 ≤ S ≤ 15). Next line contains S integers denoting the shop locations. Each of the next M lines contains three integers u, v, w (0 < u, v < N, u ≠ v, 1 ≤ w ≤ 100) denoting a road from u to v with cost w.

Output
For each case of input you have to print the case number and two integers representing maximum number of shops we can visit in the way and the minimum time required to reach junction N-1 after visiting maximum number of shops. If we cannot attend the contest, print “Impossible”. See samples for more details.

Sample Input1

2
4 4 4
0 1 2 3
0 1 10
1 3 30
0 2 30
2 3 5
4 4 4
0 1 2 3
0 1 10
3 1 30
0 2 30
3 2 5

Sample Output1

Case 1: 3 35
Case 2: Impossible

(下面是博主曾经WA掉或RE的几个数据)
Sample Input2

3
2 1 0
0 1 10
2 1 0
1 0 10
3 3 3
0 1 2
0 2 10
2 1 10
1 0 10

Sample Output2

Case 1: 0 10
Case 2: Impossible
Case 3: 3 40

博主曾经的错误

Case 1: 0 0
Case 2: RE
Case 3: 3 30

题意

一个有向图,给定几个特定的点(每个点可以经过多次),问从0出发,n-1结束最多能经过几个特定的点

Solution

先吐槽一下,为什么要从0到n-1??!?!!
本蒟蒻的习惯是1到n,而且对于解本题的算法,需要在前面后面加一个点,于是处理起来就非常麻烦了。。。
(一定是我太弱了)

好,现在进入正题
仔细读题,我们可以发现本题要求的东西与没有商店的点没有任何直接关系,于是就想到预处理
我们先把s个点一一对应的最短路求出来,于是时间复杂度就只剩 O ( s ∗ 2 s ) O(s*2^s) O(s2s)
但是现在的问题是,如何维护一个点可以走多次呢?
可以在最前面和最后面添加两个虚拟的点,作为起点和终点
现在来分析一下本人给出的数据中的第三个点

test3

我们来看一下AC的程序输出的dp数组

result

dp[3][1111]是从dp[1][1110]转移而来的,注意处理有些下标需要+1(划重点,因为我浪费了一个下午QnQ)的情况即可

/*	lightoj 1316(1)
	author - Joker
	tags - bitmasks-dp-shortest_distance
	compile time - 14:38 7/27/2018
	submit time - 15:09 7/27/2018
	status - RE
	submit time - 15:21 7/17/2018
	status - RE
	submit time - 15:40 7/27/2018
	status - WA
	submit time - 19:05 7/17/2018
	status - AC	*/

#include <cstdio>
#include <algorithm>
#include <string.h>
#define INF 0x3f3f3f3f
#define N 505
#define M 10005
#define S 17

using namespace std;

int t, n, m, s, sh[S], map[N][N], dis[N], di[S][S], dp[S][1 << S];
bool used[N];

void dijkstra(int x) {//最短路
	for (int i = 0; i < n; ++i)
		dis[i] = map[x][i];
	memset(used, 0, sizeof(used));
	used[x] = 1;
	int u;
	for (int i = 1; i < n; ++i){
		int mini = INF;
		bool flag = 1;
		for (int j = 0; j < n; ++j)
			if (used[j] == 0 && dis[j] < mini) {
				mini = dis[j];
				u = j;
				flag = 0;
			}
		if (flag) return;
		used[u] = 1;
		for (int k = 0; k < n; ++k)
			if (map[u][k] < INF) dis[k] = min(dis[k], dis[u] + map[u][k]);
	}
}

int main() {
	scanf("%d", &t);
	for (int ca = 1; ca <= t; ++ca) {
		scanf("%d%d%d", &n, &m, &s);
		for (int i = 1; i <= s; ++i) scanf("%d", &sh[i]);
		sh[0] = 0;//begin点
		sh[s + 1] = n - 1;//end点
		for (int i = 0; i < n; ++i)//初始化
			for (int j = 0; j < n; ++j)
				if (i == j) map[i][j] = 0;
				else map[i][j]= INF;
		int x, y, v;
		for (int i = 1; i <= m; ++i) {
			scanf("%d%d%d", &x, &y, &v);
			map[x][y] = min(map[x][y], v);
		}
		memset(dis, 0x3f, sizeof(dis));
		for (int i = 0; i <= s + 1; ++i) {//计算两个点之间的最短路
			dijkstra(sh[i]);
			for (int j = 0; j <= s + 1; ++j) di[i][j] = dis[sh[j]];//printf("%d\n", di[i][j]);
			//printf("\n");
		}
		for (int j = 0; j < (1 << (s + 1)); ++j){
			for (int i = 0; i <= s; ++i) {
				if (j == (1 << i)) dp[i][j] = di[0][i + 1];
				else if (j & (1 << i)) dp[i][j] = INF;
				else {
					dp[i][j] = INF;
					//printf("*\t");
					continue;
				}
				for (int k = 0; k < s; ++k)//这里不能打k<=s,因为s是终点,必须直接转移
					if (j & (1 << k)) dp[i][j] = min(dp[i][j], dp[k][j - (1 << i)] + di[k + 1][i + 1]);//就是这里要特别注意+1
				//printf("%d\n", di[1][3]);
				//printf("%d\t", dp[i][j]);
			}
			//printf("\n");
		}
		int shops = -1, dist = INF;
		for (int i = 0; i < (1 << (s + 1)); ++i) {
			if (dp[s][i] == INF || !(i & (1 << s))) continue;
			int cnt = 0;
			for (int j = 0; j < s; ++j) if (i & (1 << j)) cnt++;
			if (cnt > shops) {//更新答案
				shops = cnt;
				dist = dp[s][i];
			}
			if (cnt == shops && dist > dp[s][i]) dist = dp[s][i];//更新答案
		}
		printf("Case %d: ", ca);
		if (shops == -1) printf("Impossible\n");
		else printf("%d %d\n", shops, dist);
	}
	return 0;
}

ps:真的debug了好久好久啊啊啊啊

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值