UVALive 3353 Optimal Bus Route Design

26 篇文章 0 订阅
7 篇文章 0 订阅

UVALive 3353 Optimal Bus Route Design

二分图最大完美匹配

题目

题目给出一个带权有向图,找若干个圈,使得每个结点切好属于一个圈,并且所有圈的总长度最小,如果没有满足条件的就输出N。

思路

拆点,每个点拆成入和出。建边时比如1到2,就建1到2`。权为负的。
然后这是一个二分图图,跑二分图最大完美匹配。
注意原图不存在的边要加上,权为负无穷。
最后跑完的匹配权值要是小于等于负无穷,就是N。

代码

#include<bits/stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int MAXN=207;
const int oo=0x3f3f3f3f;
using namespace std;

int w[MAXN][MAXN], x[MAXN], slack[MAXN], y[MAXN];
int prev_x[MAXN], prev_y[MAXN], son_y[MAXN], par[MAXN];
int lx, ly, pop;
///w表示带权图
///pop表示一侧点数
///返回最大完美匹配权值
///son_y表示匹配方案
void adjust(int v)
{
    son_y[v]=prev_y[v];
    if(prev_x[son_y[v]]!=-2)
        adjust(prev_x[son_y[v]]);
}
bool fin_d(int v)
{
    for(int i=1;i<=pop;i++)
    {
        if(prev_y[i]==-1)
        {
            if(slack[i]>x[v]+y[i]-w[v][i])
            {
                slack[i]=x[v]+y[i]-w[v][i];
                par[i]=v;
            }
            if(x[v]+y[i]==w[v][i])
            {
                prev_y[i]=v;
                if(son_y[i]==-1)
                {
                    adjust(i);
                    return true;
                }
                if(prev_x[son_y[i]]!=-1) continue;
                prev_x[son_y[i]]=i;
                if(fin_d(son_y[i])) return true;
            }
        }
    }
    return false;
}
LL km()
{
    int m=-oo;
    for(int i=1;i<=pop;i++)
    {
        son_y[i]=-1;
        y[i]=0;
    }
    for(int i=1;i<=pop;i++)
    {
        x[i]=-oo;
        for(int j=1;j<=pop;j++)
            x[i]=max(x[i], w[i][j]);
    }
    bool flag;
    for(int i=1;i<=pop;i++)
    {
        for(int j=1;j<=pop;j++)
        {
            prev_x[j]=prev_y[j]=-1;
            slack[j]=oo;
        }
        prev_x[i]=-2;
        if(fin_d(i)) continue;
        flag=false;
        while(!flag)
        {
            m=oo;
            for(int j=1;j<=pop;j++)
            {
                if(prev_y[j]==-1)
                    m=min(m, slack[j]);
            }
            for(int j=1;j<=pop;j++)
            {
                if(prev_x[j]!=-1)
                    x[j]-=m;
                if(prev_y[j]!=-1)
                    y[j]+=m;
                else slack[j]-=m;
            }
            for(int j=1;j<=pop;j++)
            {
                if(prev_y[j]==-1&&!slack[j])
                {
                    prev_y[j]=par[j];
                    if(son_y[j]==-1)
                    {
                        adjust(j);
                        flag=true;
                        break;
                    }
                    prev_x[son_y[j]]=j;
                    if(fin_d(son_y[j]))
                    {
                        flag=true;
                        break;
                    }
                }
            }
        }
    }
    LL ans=0;
    for(int i=1;i<=pop;i++)
        ans+=w[son_y[i]][i];
    return ans;
}

int main()
{
    //printf("%d\n",oo);
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        pop=n;
        memset(w,0xc1,sizeof(w));
        for(int i=1;i<=n;i++)
        {
            while(1)
            {
                int u,v;scanf("%d",&u);
                if(u==0) break;
                scanf("%d",&v);
                w[i][u]=-v;
            }
        }
        LL res=km();
        if(res<=-99000) printf("N\n");
        else printf("%lld\n",-res);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值