LightOJ - 1316 A Wedding Party(最短路+状态压缩)

题目大意:有N个点,M条有向边,S个特殊点。现在要求你从0走到N-1,经过的特殊点的数量最多,且走过的路径最小

解题思路:刚开始开了一个dp[N][1 << S]的数组,可想而知,MLE了
后面想想,只需要处理出(开始点,特殊点,终点)这些点之间的距离,再用状态压缩去做就好了

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int M = 10010;
const int N = 510;
const int S = (1 << 15) + 10;
const int INF = 0x3f3f3f3f;

struct Edge{
    int u, v, dis, next;
    Edge() {}
    Edge(int u, int v, int dis, int next): u(u), v(v), dis(dis), next(next) {}
}E[M];

struct Node {
    int u, dis;
    Node() {}
    Node(int u, int dis): u(u), dis(dis) {}
    bool operator < (const Node &a) const {
        return dis > a.dis;
    }
};

struct DP{
    int pos, statu;
    DP() {}
    DP(int pos, int statu): pos(pos), statu(statu) {}
};

int head[N], dis[20][N], shop[20], dp[20][S];
bool vis[N], start_shop;
int n, m, s, cas = 1;

void init() {
    memset(head, -1, sizeof(head));

    scanf("%d%d%d", &n, &m, &s);
    int u, v, dis;
    start_shop = false;
    for (int i = 0; i < s; i++) {
        scanf("%d", &shop[i]);
        if (shop[i] == 0) start_shop = true;
    }

    sort(shop, shop + s);
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d", &u, &v, &dis);
        E[i] = Edge(u, v, dis, head[u]);
        head[u] = i;
    }
}

void dijkstra(int s, int *d) {
    for (int i = 0; i < n; i++) d[i] = INF;
    memset(vis, 0, sizeof(vis));
    priority_queue<Node> Q;
    d[s] = 0;
    Q.push(Node(s, 0));

    while (!Q.empty()) {
        int u = Q.top().u; Q.pop();

        if (vis[u]) continue;
        vis[u] = true;
        for (int i = head[u]; ~i; i = E[i].next) {
            int v = E[i].v;
            if (d[v] > d[u] + E[i].dis) {
                d[v] = d[u] + E[i].dis;
                Q.push(Node(v, d[v]));
            }
        }
    }
}

int bitcount(int x) {
    return x ? bitcount(x / 2) + x % 2: 0;
}

void solve() {
    dijkstra(0, dis[s]);
    for (int i = 0; i < s; i++)
        dijkstra(shop[i], dis[i]);
    if (dis[s][n - 1] == INF) printf("Case %d: Impossible\n", cas++);
    else {
        memset(dp, 0x3f, sizeof(dp));
        queue<DP> Q;
        Q.push(DP(s, 0));
        dp[s][0] = 0;

        int Max_shop = 0, Min_dis = INF;
        while (!Q.empty()) {
            int u = Q.front().pos;
            int statu = Q.front().statu;
            Q.pop();
            if (dis[u][n - 1] != INF) {
                int t = bitcount(statu);
                if (t > Max_shop) {
                    Max_shop = t;
                    Min_dis = dis[u][n - 1] + dp[u][statu];
                }
                else if (t == Max_shop && dp[u][statu] + dis[u][n - 1] < Min_dis) {
                    Min_dis = dp[u][statu] + dis[u][n - 1];
                }
            }

            for (int i = 0; i < s; i++) {
                if (shop[i] == 0 || statu & (1 << i)) continue;
                int sta = statu | (1 << i);
                if (dp[i][sta] > dp[u][statu] + dis[u][shop[i]]) {
                    dp[i][sta] = dp[u][statu] + dis[u][shop[i]];
                    Q.push(DP(i, sta));
                }
            }
        }
        Max_shop += start_shop;
        printf("Case %d: %d %d\n", cas++, Max_shop, Min_dis);
    }
}

int main() {
    int test;
    scanf("%d", &test);
    while (test--) {
        init();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值