—恢复内容开始—

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)\)
但是现在的问题是,如何维护一个点可以走多次呢?
可以在最前面和最后面添加两个虚拟的点,作为起点和终点
现在来分析一下本人给出的数据中的第三个点

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了好久好久啊啊啊啊

—恢复内容结束—

版权声明:本文为Fxkkks原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Fxkkks/p/9915428.html