zoukankan      html  css  js  c++  java
  • 训练指南 UVA


    layout: post
    title: 训练指南 UVA - 11383(KM算法的应用 lx+ly >=w(x,y))
    author: "luowentaoaa"
    catalog: true
    mathjax: true
    tags:
    - KM算法
    - 训练指南


    Golden Tiger Claw

    UVA - 11383

    题意

    给一个n*n的矩阵,每个格子中有正整数w[i[j],试为每行和每列分别确定一个数字row[i]和col[i],使得任意格子w[i][j]<=row[i]+col[j]恒成立。先输row,再输出col,再输出全部总和(总和应尽量小)。

    思路

    本题与匹配无关,但可以用KM算法解决。

      KM算法中的顶标就是保持了Lx[i]+ly[j]>=g[i[j]再求最大权和匹配的,但这个最大权和并没有关系。我们可以将row[i]看成一个男的,col[i]看成一个女的,这样男女的总数就相等。

      一般来说,Lx[i]或Ly[i]仅需要取该行/列中最大的那个数即可保证满足要求,但是这样太大了,可以通过调整来使得总和更小。而KM算法的过程就是一个调整的过程,每一对匹配的男女的那条边的权值就会满足等号 wi[j]=row[i]+col[j],至少需要一个来满足等号,这样才能保证row[i]+col[j]是达到最小的,即从j列看,col[j]满足条件且最小,从i行看,row[i]满足条件且最小。这刚好与KM算法求最大权和一样。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=998244353;
    const int maxn=5e2+50;
    const ll inf=1e10;
    const ll INF = 1000000000;
    const double eps=1e-5;
    int g[530][530];  ///存图
    int nx,ny; /// 两边点数
    bool visx[maxn],visy[maxn];
    int slack[maxn];
    int linker[maxn];   ///y中各点匹配状态
    int lx[maxn],ly[maxn]; /// x,y中的点标号
    bool dfs(int x){
        visx[x]=true;
        for(int y=0;y<ny;y++){
            if(visy[y])continue;
            int tmp=lx[x]+ly[y]-g[x][y];
            if(tmp==0){
                visy[y]=true;
                if(linker[y]==-1||dfs(linker[y])){
                    linker[y]=x;return true;
                }
            }
            else if(slack[y]>tmp)slack[y]=tmp;
        }
        return false;
    }
    int KM(){
        memset(linker,-1,sizeof(linker));
        memset(ly,0,sizeof(ly));
        for(int i=0;i<nx;i++){
            lx[i]=-inf;
            for(int j=0;j<ny;j++){
                if(g[i][j]>lx[i])lx[i]=g[i][j];
            }
        }
        for(int x=0;x<nx;x++){
            for(int i=0;i<ny;i++)slack[i]=inf;
            while(true){
                memset(visx,false,sizeof(visx));
                memset(visy,false,sizeof(visy));
                if(dfs(x))break;
                int d=inf;
                for(int i=0;i<ny;i++)
                    if(!visy[i]&&d>slack[i])d=slack[i];
                for(int i=0;i<nx;i++)
                    if(visx[i])lx[i]-=d;
                for(int i=0;i<ny;i++)
                    if(visy[i])ly[i]+=d;
                    else slack[i]-=d;
            }
        }
        int res=0;
        for(int i=0;i<ny;i++)
            if(linker[i]!=-1)res+=g[linker[i]][i];
        return res;
    }
    
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        std::cout.tie(0);
        int n;
        while(cin>>n){
            nx=ny=n;
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)cin>>g[i][j];
            int ans=KM();
            cout<<lx[0];
            for(int i=1;i<n;i++)cout<<" "<<lx[i];cout<<endl;
            cout<<ly[0];
            for(int i=1;i<n;i++)cout<<" "<<ly[i];cout<<endl;
            cout<<ans<<endl;
        }
    
        return 0;
    }
    
    
  • 相关阅读:
    转:单链表有环判断及其起始位置定位
    转:C++经典排序算法总结
    转:堆排序
    转载:C++快速排序
    转载:平衡二叉树(AVL Tree)
    设计模式原则
    适配器模式
    单例模式
    工厂模式
    Head First设计模式 装饰者模式
  • 原文地址:https://www.cnblogs.com/luowentao/p/10351197.html
Copyright © 2011-2022 走看看