[BZOJ3118]Orz the MST --ORZ the Simplex

题意

给定 N 个点以及M条边,每条边可以花费一定的代价修改权值,求需要花费的最小代价使得规定的 N1 条边为这张图的最小生成树。


把规定的 N1 条边构成一棵树,那么其他的边如果加入这张图中,必定会构成一个环。

Xi 为第 i 条边修改的权值量,Wi为第 i 条边原始权值。

对于不需要的边i,dfs出它加入图后构成的环,环中除了这条边以外的边 j 必须满足
WjXjWi+Xj

那么就可以线性规划来做这道题。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#define N 310
#define M 1010
#define inf (1<<30)
#define eps 1e-7

using namespace std;

int n,m,x,y,w,f,a,b,tt,tt0,G[N],tail,cnt;
double A[M][M<<2],B[M],C[M<<2],v;

struct edge{
    int nx,t,w,x,g;
}E[M<<2],Q[M<<1];

struct nteg{
    int l,r,w,x,g;
}ne[M];

inline void reaD(int &x){
    char Ch=getchar();x=0;
    for(;Ch>'9'||Ch<'0';Ch=getchar());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}

inline void InserT(int x,int y,int w,int a,int g){
    E[++tt0].nx=G[x];E[tt0].t=y;E[tt0].w=w;E[tt0].x=a;E[tt0].g=g;G[x]=tt0;
    E[++tt0].nx=G[y];E[tt0].t=x;E[tt0].w=w;E[tt0].x=a;E[tt0].g=g;G[y]=tt0;
}

bool dfs(int x,int y,int l){
    if(x==y) return true;
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=l){
            Q[++tail]=E[i];
            if(dfs(E[i].t,y,x))return true;
            tail--;
        }
    return false;
}

void build(nteg x){
    tail=0,dfs(x.l,x.r,0);
    for(int i=1;i<=tail;i++) A[Q[i].g][++cnt]=1,A[x.g][cnt]=1,C[cnt]=Q[i].w-x.w;
}

inline void pivot(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){
        B[i]-=A[i][e]*B[l];
        for(int j=1;j<=n;j++)if(j!=e) A[i][j]-=A[i][e]*A[l][j];
        A[i][e]*=-A[l][e];
    }
    v+=B[l]*C[e];
    for(int i=1;i<=n;i++)if(i!=e)C[i]-=C[e]*A[l][i];
    C[e]*=-A[l][e];
}

inline int Simplex(){
    int i,l,e;
    while(1){
        for(i=1;i<=n;i++)
            if(C[i]>eps) break;
        if((e=i)>n) return (int)(v+0.5);
        double tmp=inf;
        for(int i=1;i<=m;i++)
            if(A[i][e]>eps&&tmp>B[i]/A[i][e]) tmp=B[i]/A[i][e],l=i;
        if(tmp==inf) return inf;
        pivot(l,e); 
    }
}

int main(){
    reaD(n);reaD(m);
    for(int i=1;i<=m;i++){
        reaD(x);reaD(y);reaD(w);reaD(f);reaD(a);reaD(b);
        if(f) InserT(x,y,w,b,i),B[i]=b;
        else ne[++tt]=(nteg){x,y,w,a,i},B[i]=a;
    }
    for(int i=1;i<=tt;i++) build(ne[i]);
    n=cnt;
    return printf("%d\n",Simplex()),0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值