题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6141
题意: 要你求一个最大树形图, 在最大化边权和的前提下最小化编号为n的点的父亲编号, 输出边权和与编号为n的点的父亲编号, 保证有解。( n≤1000,m≤10000 )
思路: 自己在模版上怎么改也改不对。 其实就是把边权都乘个10000, 把所有指向n的点的点编号弄到边权里就行了。 最后就是一个裸的求最小树形图的权值的板子。这种巧妙的处理方法也是一个套路吧。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define ll long long
const int N = (int)1e3 + 10;
const int M = (int)1e4 + 10;
using namespace std;
int n, _n, m;
int E[M][2]; ll cst[M];
int id[N], pre[N], vis[N]; ll in[N];
void Directed_MST(int rt){
ll ret = 0; int cnt;
memset(in, 0, sizeof(in));
while (1){
for (int i = 1; i <= n; i ++) pre[i] = 0;
for (int i = 1; i <= m; i ++){
int u = E[i][0], v = E[i][1];
if (u == v) continue;
if (!pre[v] || in[v] < cst[i]) in[v] = cst[i], pre[v] = u;
}
memset(id, 0, sizeof(id));
memset(vis, 0, sizeof(vis));
in[rt] = 0;
cnt = 0;
for (int i = 1; i <= n; i ++){
ret += in[i]; int v = i;
while (vis[v] != i && !id[v] && v != rt){vis[v] = i; v = pre[v];}
if (!id[v] && v != rt){
cnt ++;
for (int u = pre[v]; u != v; u = pre[u]) id[u] = cnt;
id[v] = cnt;
}
}
if (cnt == 0) break;
for (int i = 1; i <= n; i ++) if (!id[i]) id[i] = ++ cnt;
for (int i = 1; i <= m; i ++){
int v = E[i][1];
E[i][0] = id[E[i][0]];
E[i][1] = id[E[i][1]];
if (E[i][0] != E[i][1]) cst[i] -= in[v];
}
rt = id[rt]; n = cnt;
}
printf("%lld %lld\n", ret / 10000, _n - (ret % 10000) + 1);
}
int main(){
int T; T = 0;
for (scanf("%d", &T); T --; ){
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i ++){
scanf("%d %d %lld", E[i] + 0, E[i] + 1, cst + i);
cst[i] *= 10000;
if (E[i][1] == n) cst[i] += n - E[i][0] + 1;
}
_n = n;
Directed_MST(1);
}
return 0;
}