文章目录
The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest
A. attack
**题意: ** 给定一张n个点m条边的无向带权图,给定4个点对。要求构造一个森林,使得这4对点中的2个点都能连接起来。 1 < = n < = 30 , 1 < = m < = 1000 1<=n<=30, 1<=m<=1000 1<=n<=30,1<=m<=1000
题解: 本题是斯坦纳森林,明确了哪个关键点和哪个关键点连接起来,只需要改变check函数的逻辑即可。check函数要求对应的关键点要同时存在或者同时不存在,其他的按照斯坦纳森林的套路转移即可。之前第一次做这个题目,自己floyd转移,打了半天一直wa,后来发现是算法错了。
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 35, K = 10, M = 2010;
int n, m, k, f[N][1 << K], f2[1 << K], T, cnt;
int e[M], ne[M], h[N], idx, w[M];
queue <int> q;
bool st[N];
vector<string> v1;
struct Edge {
string u, v;
int val;
}edge[M];
unordered_map<string, int> mp;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
// spfa枚举树上边进行松弛
void spfa(int S) {
while(q.size()) {
int t = q.front();
q.pop();
st[t] = 0;
for(int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if(f[j][S] > f[t][S] + w[i]) {
// 当前状态下的点的转移
f[j][S] = f[t][S] + w[i];
if(!st[j]) {
st[j] = 1;
q.push(j);
}
}
}
}
}
int mapping(string x) {
if (!mp.count(x)) mp[x] = ++cnt;
return mp[x];
}
bool check(int state) {
// check函数保证了对应关键点同时出现和消失
for (int i = 0; i < 4; ++i) {
if ((state >> i & 1) && !(state >> (7 - i) & 1)) return false;
if ((state >> (7 - i) & 1) && !(state >> i & 1)) return false;
}
return true;
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m; // 点数、边数、关键点数
k = 8;
for (int i = 1; i <= n; ++i) {
// 先把点和边都离线下来
string u;
cin >> u;
v1.push_back(u);
}
int tot = 0;
for (int i = 1; i <= m; ++i) {
string a, b;
int c;
cin >> a >> b >> c;
edge[tot++] = {
a, b, c};
}
memset(f, 0x3f, sizeof(f));// 初始化
for (int i = 1; i <= 4; ++i) {
string a, b