UVA12507 Kingdoms 题解

题目翻译(管理顺便把翻译过一下呗/kel)

题目传送

更好的阅读体验?

Solution

发现 \(n\) 的数据范围很小,考虑状压。

但我不会转移方程,于是结合一点 SPFA 和 Prim 的思想来搞。

\(f_{i}\) 表示到达 \(i\) 状态所需的最少的资金。状态 \(i\) 表示,如果 \(i\) 的第 \(x\) 位为 \(1\),表示城市 \(x\)\(1\) 相连通。

把每个状态看成点跑 SPFA,显然初始状态为 \(f_1 = 0\)

怎么转移?

我们可以让前一个状态选中的所有点向周围能到达的所有点扩展,从而到达一个新状态。

枚举所有是 \(1\) 的二进制位,假设是第 \(x\) 位,如果有一条边 \((x,v)\) 就可以尝试转移。转移方程如下:

\[f_{i | (1<<v)} = \min\{f_{i} + dis(x,v) \}(i\&(1<<x)=1) \]

按照 SPFA 的松弛操作来跑即可。

数据比较弱,当前是最优解。

Code

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct edge {
    int to, w, nxt;
}e[300 << 1];
int head[20], num_edge = 1;

int T, n, m, K;
int val[20], f[MAXN];
bool vis[MAXN];
queue<int> q;

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

void add_edge(int from, int to, int w) { e[++num_edge] = (edge){to, w, head[from]}, head[from] = num_edge; }

void SPFA() {
    memset(f, 0x3f, sizeof f);
    f[1] = 0;
    q.push(1), vis[1] = true;
    while(!q.empty()) {
        int S = q.front(); q.pop();
        vis[S] = false;
        for(int i = 1; i <= n; ++i) {
            if(S & (1 << (i - 1))) {
                for(int j = head[i]; j; j = e[j].nxt) {
                    int v = e[j].to, now_ = (S | (1 << v - 1));
                    if(f[now_] > f[S] + e[j].w) {
                        f[now_] = f[S] + e[j].w;
                        if(!vis[now_]) q.push(now_), vis[now_] = true;
                    }
                }
            }
        }
    }
}

int main()
{
    T = read();
    while(T--) {
        memset(head, false, sizeof head);
        num_edge = 1;
        n = read(), m = read(), K = read();
        for(int i = 1; i <= n; ++i) val[i] = read();
        for(int i = 1, u, v, w; i <= m; ++i) {
            u = read(), v = read(), w = read();
            add_edge(u, v, w), add_edge(v, u, w);
        }
        SPFA();
        int Ans = -1;
        for(int i = 1; i < (1 << n); ++i) {
//            cout<<i<<" "<<f[i]<<"\n";
            if(f[i] > K) continue;
            int sum = 0;
            for(int j = 1; j <= n; ++j) if(i & (1 << j - 1)) sum += val[j];
            Ans = max(Ans, sum);
        }
        printf("%d\n", Ans);
    }
    return 0;
}
posted @ 2021-07-15 08:33  Suzt_ilymtics  阅读(50)  评论(0编辑  收藏  举报