Description
Input
Output
Sample Input
6 8
1 2 3 1 4 2
1 4 2 0 3 4
2 3 5 1 2 1
2 4 4 1 3 5
3 5 2 0 1 3
3 6 1 0 2 4
4 5 7 1 3 2
5 6 5 1 5 4
Sample Output
HINT
样例解释:
最优方案为:(1, 4)边权加2,代价6;(3, 5)边权加3,代价3;(3, 6)边权加4,代价8;(4, 5)边权减2,代价4;总代价21。
数据范围:
1<=N<=300, 1<=M, Wi, Ai, Bi<=1000。
分析:
线性规划
显然:树边只可能减权,非树边只可能加权
我迟迟没有没有写出不等式的原因就是:树边中最大的一条边比非树边最小的一条边小,但是哪一条是最小边,哪一条是最大边呢
后来我就想,实际上我们没有必要找到确定到底是哪一条边,我们只要把所有非树边和树边都互相比较一下即可
那么就列式子吧:
对于一条树边
wi
w
i
和一条非树边
wj
w
j
都有:
wi−xi<=wj+xj
w
i
−
x
i
<=
w
j
+
x
j
移向得到:
wi−wj<=xi+xj
w
i
−
w
j
<=
x
i
+
x
j
这样就可以搞了
写完了之后,样例一直过不去
难道有纰漏?
确实,我一开始的假设就错了:树边中最大的一条边比非树边最小的一条边小
想一下朴素构造最小生成树时,我们虽然是从小到大枚举每一条边
但是如果当前边的两个顶点已经连通,我们就会跳过这条边
因此树中的边不一定都比非树边小
那我们怎么改进我们算法呢?
如果我们已经有了一棵最小生成树,现在要加入一条边(x,y),在什么情况下,这条边会代替生成树中的边呢?
显然,当在树中x—>y的路径中有一条边比当前边的权值大
因此我们要保证路径上的所有边最终权值小于等于这条非树边
按照这个条件构造线性规划矩阵
每个式子的形式应该都是这样的:
xi+xj>=wi−wj
x
i
+
x
j
>=
w
i
−
w
j
构造对偶图
用单纯形算法解决即可
tip
约定条件的常数中不要有负数
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const double INF=1e10;
const double eps=1e-7;
const int N=1005;
double ans=0.0,a[N][N*10],b[N],c[N*10];
int n,m,pre[N],cnt,st[N],tot=0,deep[N],pos[N];
struct node{
int x,y,v,nxt,id;
};
node way[N];
struct po{
int x,y,v,type;
};
po e[N];
void add(int i,int u,int w,int z)
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot; way[tot].id=i;
tot++;
way[tot].x=w;way[tot].y=u;way[tot].v=z;way[tot].nxt=st[w];st[w]=tot; way[tot].id=i;
}
void priov(int l,int e)
{
b[l]/=a[l][e];
for (int i=1;i<=m;i++) if (i!=e)
a[l][i]/=a[l][e];
a[l][e]=1/a[l][e];
for (int i=1;i<=n;i++) if (i!=l&&fabs(a[i][e])>eps)
{
b[i]-=b[l]*a[i][e];
for (int j=1;j<=m;j++) if (j!=e)
a[i][j]-=a[l][j]*a[i][e];
a[i][e]=-a[i][e]*a[l][e];
}
ans+=c[e]*b[l];
for (int i=1;i<=m;i++) if (i!=e)
c[i]-=c[e]*a[l][i];
c[e]=-c[e]*a[l][e];
}
void simple()
{
int i,l,e;
while (1)
{
for (i=1;i<=m;i++)
if (c[i]>eps) break;
e=i;
if (e==m+1) break;
double t=INF;
for (i=1;i<=n;i++)
if (a[i][e]>eps&&t>b[i]/a[i][e])
t=b[i]/a[i][e],l=i;
if (t==INF) break;
priov(l,e);
}
}
void dfs(int now,int fa,int dep)
{
deep[now]=dep;
pre[now]=fa;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa)
{
pos[way[i].y]=way[i].id; //连接x和fa的边
dfs(way[i].y,now,dep+1);
}
}
void solve(int i,int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
while (deep[x]>deep[y])
{
if (e[pos[x]].v>e[i].v)
{
cnt++;
a[i][cnt]++; a[pos[x]][cnt]++;
c[cnt]=(double)e[pos[x]].v-e[i].v;
}
x=pre[x];
}
while (x!=y)
{
if (e[pos[x]].v>e[i].v)
{
cnt++;
a[i][cnt]++; a[pos[x]][cnt]++;
c[cnt]=(double)e[pos[x]].v-e[i].v;
}
x=pre[x];
if (e[pos[y]].v>e[i].v)
{
cnt++;
a[i][cnt]++; a[pos[y]][cnt]++;
c[cnt]=(double)e[pos[y]].v-e[i].v;
}
y=pre[y];
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int A,B;
scanf("%d%d%d%d%d%d",&e[i].x,&e[i].y,&e[i].v,&e[i].type,&A,&B);
if (e[i].type) {
add(i,e[i].x,e[i].y,e[i].v);
b[i]=(double)B; //树边只减
}
else b[i]=(double)A; //非树边只加
}
dfs(1,0,1);
cnt=0;
for (int i=1;i<=m;i++) if (e[i].type==0)
solve(i,e[i].x,e[i].y);
n=cnt; swap(n,m);
simple();
printf("%.0lf",ans);
return 0;
}