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;
}