题意:HDU-4085
给 n n n个点,需要你通过边来连接前 k k k个点和后 k k k个点,求最小总边权。
分析:
这题比较坑的是最后你得出来的图可以是不连通的(每个点对一一对应也是满足题意的)…也就是说你可以这样, 1 1 1点只与 n n n(与 n − 1 n-1 n−1也可以)点连接, 2 2 2点只与 n − 1 n-1 n−1点连接等等。你也可以将所有 2 k 2k 2k点通过边连通起来,但是答案不一定最优。所以我们先跑一遍斯坦纳树,然后求一遍合法子树的最小值,最后再 d p dp dp合并斯坦纳子树寻找最优解。
p s : ps: ps:一开始以为这 2 k 2k 2k个点都要连通, w a wa wa了 n n n次, q a q qaq qaq。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define pii pair<int, int>
#define ppi pair<pii, int>
#define mp make_pair
#define fi first
#define se second
const int maxn = 50 + 5;
const int maxm = (1 << 10) + 5;
const int inf = 0x3f3f3f3f;
int n, m, k, u, v, w, cnt, ans, maxsta;
int head[maxn], dp[maxn][maxm], DP[maxm];
bool vis[maxn];
queue<int> q;
struct node{
int to, val, next;
}edge[maxm << 1];
void addedge(int u, int v, int w){
edge[cnt].to = v;
edge[cnt].val = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void spfa(int sta){
while(!q.empty()){
int u = q.front();
q.pop();
vis[u] = 0;
for(int i = head[u]; ~i; i = edge[i].next){
int to = edge[i].to, val = edge[i].val;
if(dp[to][sta] > dp[u][sta] + val){
dp[to][sta] = dp[u][sta] + val;
if(!vis[to]) q.push(to), vis[to] = 1;
}
}
}
}
bool check(int sta){//判断这个状态是不是一一对应
int ans = 0;
for(int i = 0; i < 2 * k; i++){
if(i < k) (1 << i) & sta ? ans++ : 0;
else (1 << i) & sta ? ans-- : 0;
}
return !ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T--){
cin >> n >> m >> k;
memset(head, -1, sizeof head), cnt = 0;
for(int i = 1; i <= m; i++){
cin >> u >> v >> w;
addedge(u, v, w);
addedge(v, u, w);
}
memset(dp, inf, sizeof dp);
for(int i = 1; i <= k; i++) dp[i][1 << (i - 1)] = 0;
for(int i = n - k + 1, j = k + 1; i <= n; i++, j++) dp[i][1 << (j - 1)] = 0;
maxsta = 1 << (2 * k);
for(int sta = 0; sta < maxsta; sta++){
while(!q.empty()) q.pop();
for(int i = 1; i <= n; i++){
for(int s = sta; s; s = (s - 1) & sta){
if(dp[i][sta] > dp[i][s] + dp[i][s ^ sta]){
dp[i][sta] = dp[i][s] + dp[i][s ^ sta];
}
}
if(dp[i][sta] < inf) q.push(i), vis[i] = 1;
}
spfa(sta);
}
memset(DP, inf, sizeof DP);
for(int s = 0; s < maxsta; s++){
if(!check(s)) continue;
for(int i = 1; i <= n; i++)
DP[s] = min(DP[s], dp[i][s]);
}
for(int s = 0; s < maxsta; s++){
if(!check(s)) continue;
for(int p = (s - 1) & s; p; p = (p - 1) & (s))
DP[s] = min(DP[s], DP[p] + DP[s ^ p]);
}
if(DP[maxsta - 1] == inf) cout << "No solution" << '\n';
else cout << DP[maxsta - 1] << '\n';
}
return 0;
}