zoj 2342 Roads

  在最优策略中,我们只会减少石头路的费用,增加烂泥路的费用,设这个改变量为 l(x) 。由于前 n1 条边是生成树,对第 n ~m条边中的每一条,它的加入会形成一个圈。在最优解的情况下,圈中的任意一条石头路费用不大于烂泥路的费用。把石头路看作二分图的X部,烂泥路看作Y部,可以得到关于改变量 l(x) 的不等式。这个模型直接套二分图最优匹配就行,求出的可行顶标就是改变量。
  KM算法还是略神奇的,有待加强理解!

#include <bits/stdc++.h>

using namespace std;

const int maxn = 66;
const int maxN = 410;

int lx[maxN];
int ly[maxN];
int sx[maxN];
int sy[maxN];
int match[maxN];
int weight[maxN][maxN];
int sz;

bool path(int u){

    sx[u] = true;
    for(int v=1;v<=sz;v++){
        if(!sy[v] && lx[u] + ly[v] == weight[u][v]){
            sy[v] = true;
            if(match[v] == -1 || path(match[v])){
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

int bestmatch(){
    int i,j;
    for(i=1;i<=sz;i++){
        lx[i] = 0x80000000;
        ly[i] = 0;
        for(j=1;j<=sz;j++){
            if(lx[i]<weight[i][j]){
                lx[i] = weight[i][j];
            }
        }
    }
    memset(match,-1,sizeof(match));

    for(int u=1;u<=sz;u++){
        while(1){
            memset(sx,0,sizeof(sx));
            memset(sy,0,sizeof(sy));
            if(path(u)){
                break;
            }
            int dx = 0x7fffffff;
            for(i=1;i<=sz;i++){
                if(sx[i]){
                    for(j=1;j<=sz;j++){
                        if(!sy[j]){
                            dx = min(lx[i]+ly[j]-weight[i][j],dx);
                        }
                    }
                }
            }
            for(i=1;i<=sz;i++){
                if(sx[i]){
                    lx[i] -= dx;
                }
                if(sy[i]){
                    ly[i] += dx;
                }
            }
        }
    }
    int sum = 0;
    for(i=1;i<=sz;i++){
        sum+=weight[match[i]][i];
    }
    return sum;
}

vector<int> adj[maxn];

int height[maxn];
int p[maxn];
int pEdge[maxn];

struct Edge{
    int u;
    int v;
    int w;
    Edge(int u,int v,int w):u(u),v(v),w(w){
    }
    Edge(){
    }
}edges[maxN];

void predfs(int u){
    for(int i=0;i<adj[u].size();i++){
        int v = edges[adj[u][i]].u;
        if(v==u){
            v = edges[adj[u][i]].v;
        }
        if(v!=p[u]){
            p[v] = u;
            pEdge[v] = adj[u][i];
            height[v] = height[u] + 1;
            predfs(v);
        }
    }
}

void init(){
    memset(weight,0,sizeof(weight));
    memset(adj,0,sizeof(adj));
    memset(p,0,sizeof(p));
}

void addedge(int u,int v,int id){
    adj[u].push_back(id);
    adj[v].push_back(id);
}

int uu[maxN];
int vv[maxN];
int ww[maxN];

int main(){
    int t;
    cin>>t;
    while(t--){
        init();

        int n,m;
        cin>>n>>m;
        for(int i=1;i<=m;i++){
            cin>>uu[i]>>vv[i]>>ww[i];
            if(i<n){
                edges[i] = Edge(uu[i],vv[i],ww[i]);
                addedge(uu[i],vv[i],i);
            }else{
            }
        }

        predfs(1);

        int szX = n-1;
        int szY = m-n+1;
        sz = max(szX,szY);
        int Yid = 1;

        for(int i=n;i<=m;i++){
            int u = uu[i];
            int v = vv[i];
            if(height[u]>height[v]){
                swap(u,v);
            }

            int Xid;
            int sum;

            // move v
            while(height[v] > height[u]){
                Xid = pEdge[v];
                sum = ww[Xid] - ww[i];
                if(sum<0){
                    sum = 0;
                }
                weight[Xid][Yid] = sum;
                v = p[v];
            }

            //move u and v
            while(u!=v){
                Xid = pEdge[u];
                sum = ww[Xid] - ww[i];
                if(sum<0){
                    sum = 0;
                }
                weight[Xid][Yid] = sum;

                Xid = pEdge[v];
                sum = ww[Xid] - ww[i];
                if(sum<0){
                    sum = 0;
                }
                weight[Xid][Yid] = sum;

                u = p[u];
                v = p[v];
            }

            Yid++;
        }

/*
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                cout<<weight[i][j]<<" ";
            }
            cout<<endl;
        }
*/

        bestmatch();

        for(int i=1;i<=szX;i++){
            cout<<ww[i] - lx[i]<<endl;
        }
        for(int i=1;i<=szY;i++){
            cout<<ww[i+n-1] + ly[i]<<endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值