题意:
给出一个无向连通图,并指定其中一颗生成树;
每条边上有一个权值vali,如果增大这个权值1则花费Ai,减小1花费Bi;
现在要使指定的生成树为一颗最小生成树,求最小花费;
n<=300,m<=1000;
题解:
一道线性规划比较神的题目,前面刷的比较偏水就不刷了;
首先有一点极为显然的东西(我居然没看出来),树上的边一定减小权值,非树上的边一定增大权值;
然后考虑对于一颗生成树要为最小要满足的条件,也就是本题的约束条件;
如同Tarjan算法一样,每一条非树边都会在树上生成一个环;
而如果这个环上的某个边权值比它大,那么这个环就可以从那条边处断开,且生成树更小;
也就是说对于一个非树边,环上边都要小于等于它;
找约束的过程我写的似乎比较蠢,深搜记了一堆再LCA;
不过无论如何最坏也不过n*m总归不会因此TLE;
设xi为第i条边的改变量,那么树边的即为减小量,非树边的为增大量;
可得方程:vali+xi<=valj-xj (i为树边j为非树边,且i,j在一个环上);
移项:xi+xj<=valj-vali;
现在的线性规划为:
Min∑costi*xi
xi+xj<=valj-vali
显然它不存在基本可行解,那么做一些变形;
首先将目标函数取反:
Max∑ -costi*xi
xi+xj<=valj-vali
然后对偶原理!
Max∑(valj-vali)*x(j,i)
%*&^%*>=-costi
再对不等式变号:
Max∑(valj-vali)*x(j,i)
-%*&^%*<=costi
现在就可以发现,线性规划已经是标准型了;
而题中的cost都是正数,也就是这个线性规划有基本可行解咯;
然后上单纯型直接搞这东西,题目性质也保证了这东西不会无界;
时间复杂度O(单纯型(n*m,m));
显然是会T死的,然而约束并不会有最坏情况n*m这么多。。
所以还是可以过的啦,n*m的数组开到4000能A;
代码:
#include<vector> #include<math.h> #include<stdio.h> #include<string.h> #include<algorithm> #define N 310 #define M 1100 using namespace std; const double EPS = 1e-8; const double INF = 1e100; struct node { int n, m; double a[M][M << 2], b[M], c[M << 2], v; int find() { for (int i = 1; i <= n; i++) { if (c[i] > EPS) return i; } return 0; } void Rotate(int l, int e) { b[l] /= a[l][e]; for (int i = 1; i <= n; i++) { if (i != e) a[l][i] /= a[l][e]; } a[l][e] = 1 / a[l][e]; for (int i = 1; i <= m; i++) { if (i == l || fabs(a[i][e]) < EPS) continue; b[i] -= b[l] * a[i][e]; for (int j = 1; j <= n; j++) { if (j != e) a[i][j] -= a[l][j] * a[i][e]; } a[i][e] *= -a[l][e]; } v += c[e] * b[l]; for (int i = 1; i <= n; i++) { if (i != e) c[i] -= c[e] * a[l][i]; } c[e] *= -a[l][e]; } void Simplex() { int i, j, k, l, e; while (e = find()) { double lim = INF; for (i = 1; i <= m; i++) { if (a[i][e] < EPS) continue; if (lim>b[i] / a[i][e]) lim = b[i] / a[i][e], l = i; } Rotate(l, e); } } }T; vector<int>to[N], val[N], cost[N], no[N]; vector<bool>cov[N]; int fa[N], prec[N], prev[N], preno[N], tim[N], deep[N], tot; void Build(int x, int y, int val, int no) { if (deep[x] < deep[y]) swap(x, y); while (deep[x]>deep[y]) { T.c[++T.n] = -(val - prev[x]); T.a[no][T.n] = 1; T.a[preno[x]][T.n] = 1; x = fa[x]; } while (x != y) { T.c[++T.n] =-( val - prev[x]); T.a[no][T.n] = 1; T.a[preno[x]][T.n] = 1; x = fa[x]; T.c[++T.n] = -(val - prev[y]); T.a[no][T.n] = 1; T.a[preno[y]][T.n] = 1; y = fa[y]; } } void dfs(int x, int pre, int v, int num, int d) { int i, y; fa[x]=pre,prev[x] = v, preno[x] = num, tim[x] = ++tot, deep[x] = d; for (i = 0; i < to[x].size(); i++) { if (cov[x][i]&&(y=to[x][i])!=pre) dfs(y, x, val[x][i], no[x][i], d + 1); } for (i = 0; i < to[x].size(); i++) { if (!cov[x][i] && tim[y = to[x][i]] && tim[y] < tim[x]) { Build(x, y, val[x][i], no[x][i]); } } } int main() { int n, m, i, j, k, x, y, v, f, a, b; scanf("%d%d", &n, &m); for (i = 1; i <= m; i++) { scanf("%d%d%d%d%d%d", &x, &y, &v, &f, &a, &b); to[x].push_back(y), val[x].push_back(v), cov[x].push_back(f), no[x].push_back(i); to[y].push_back(x), val[y].push_back(v), cov[y].push_back(f), no[y].push_back(i); if (f) T.b[i] = b; else T.b[i] = a; } dfs(1, 0, 0, 0, 1); T.m=m; T.Simplex(); printf("%.0lf",T.v); return 0; }