【网络流】hdu3488 Tour

题意:在一个有向图中找到一些环,使得图上的每个点都在这些环中的某个环中出现过一次。
难度:2
题解:二分图最优匹配(模板再次TLE)。把每个点i拆成Xi和Yi,新增一个附加源s和一个附加汇t,s连每一个Xi以及每一个Yi连t一条容量为1,费用为0的边;对于每一条边的关系:u->v,从Xu向Yv连一条容量为1,费用为该边权值的边,求最大流。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
int n,m;
const int maxn = 205;
const int INF = 0xffffff;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int linky[maxn];
int visx[maxn],visy[maxn];
int slack[maxn];
bool find(int x) {
    visx[x] = true;
    for(int y=1;y<=n;y++) {
        if(visy[y]) 
            continue;
        int t = lx[x]+ly[y]-w[x][y];
        if(t == 0) {
            visy[y] = true;
            if(linky[y] == -1 || find(linky[y])) {
                linky[y] = x;
                return true;    
            }    
        }    
        else if(slack[y] > t)
            slack[y] = t;
    }    
    return false;
}
int KM() {//返回最优匹配得知 
    int i,j;
    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i=1;i<=n;i++)
        for(j=1,lx[i]=-INF;j<=n;j++)
            if(w[i][j] > lx[i])
                lx[i] = w[i][j];
    for(int x=1;x<=n;x++) {
        for(i=1;i<=n;i++)
            slack[i] = INF;
        while(true) {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(find(x)) 
                break;
            int d = INF;
            for(i=1;i<=n;i++) {
                if(!visy[i] && d > slack[i])
                    d = slack[i];   
            }
            for(i=1;i<=n;i++) {
                if(visx[i])
                    lx[i] -= d;    
            }
            for(i=1;i<=n;i++) {
                if(visy[i]) ly[i] += d;
                else slack[i] -= d;    
            }
        }    
    }
    int result = 0;
    for(i=1;i<=n;i++) {
        if(linky[i] == -1 || w[linky[i]][i] == -INF)
            return 1;
        else 
            result += w[linky[i]][i];    
    }
    return result;
}
int main() {
    int tcase;
    int i,j;
    cin>>tcase;
    while(tcase--) {
        cin>>n>>m;
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                w[i][j]=-INF;
        int a,b,c;
        while(m--) {
            cin>>a>>b>>c;
            if(w[a][b] < -c) 
                w[a][b] = -c;    
        }   
        cout<<-KM()<<endl;
    }
    return 0;    
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值