2018百度之星程序设计大赛资格赛——1003整数规划

HDU6346

Problem Description

度度熊有一个可能是整数规划的问题:

给定 n×n 个整数 ai,j(1≤i,j≤n),要找出 2n 个整数 x1,x2,…,xn,y1,y2,…,yn 在满足 xi+yj≤ai,j(1≤i,j≤n) 的约束下最大化目标函数 ∑ni=1xi+∑ni=1yi,

你需要帮他解决这个整数规划问题,并给出目标函数的最大值。

Input

第一行包含一个整数 T,表示有 T 组测试数据。

接下来依次描述 T 组测试数据。对于每组测试数据:

第一行包含一个整数 n,表示该整数规划问题的规模。

接下来 n 行,每行包含 n 个整数,其中第 i 行第 j 列的元素是 ai,j。

保证 1≤T≤20,1≤n≤200,−109≤ai,j≤109(1≤i,j≤n)。

Output

对于每组测试数据,输出一行信息 "Case #x: y"(不含引号),其中 x 表示这是第 x 组测试数据,y 表示目标函数的最大值,行末不要有多余空格。

Sample Input

 

2 1 0 2 1 2 3 4

Sample Output

 

Case #1: 0 Case #2: 5

这是二分图的最优完备匹配问题,也就是km(Kuhn——Munkres)算法的模版题。

从n=2看起:

目标函数:x1+x2+y1+y2

把x和y进行匹配可得两种情况:1.  x1,y1和x2,y2 ; 2. x1,y2和x2,y1

对于第一种,最大有:a11+a22;

对于第二种,最大有:a12+a21;

因为对于任意的i、j ,都要有 xi+yj≤aij , 所以所求值应该为:min { a11+a22 ,a12+a21 }

二分图:即对于一个图中的所有顶点,可以将所有顶点分成两个互不相交的集合A、B。集合A和集合B中的点连接形成图的边,但同一集合内的点不能互相连接。

km算法:是 匈牙利算法的扩展 ,用于求二分图的最大权值完美匹配。

( km算法网上有很多讲解都讲得挺好的,通过一些像是 “男女匹配找对象”、“做工作” 的例子可以很轻松的理解。但具体数学上证明尚未深入了解......)

这里求的是最小值,只需将每条边权值取相反数,得到最大值后再取相反数即可。

代码:(dfs实现)

这里的dfs模版要够硬才过得去,不然很容易就TLE。首先是用slack函数来优化时间复杂度到O(n3),然后还需要通过在深搜加上一个flag参数做优化,然后在函数km()里做判断来减少匹配的次数。也可以用bfs来做。

//
//  main.cpp
//  P1003
//
//  Created by jinyu on 2018/8/10.
//  Copyright © 2018年 jinyu. All rights reserved.
//

#include <iostream>
#include <algorithm>

using namespace std;
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b

const int MAXN = 200+7;
const int INF = 0x3f3f3f3f;

int N;
int axy[MAXN][MAXN];
int ex_X[MAXN];
int ex_Y[MAXN];
int match[MAXN];
int slack[MAXN];
bool visX[MAXN];
bool visY[MAXN];

inline bool dfs(int x , int flag){
    
    visX[x] = true;
    for(int i = 0;i<N;++i){
        
        if(visY[i])     continue;
        
        int temp = ex_X[x] + ex_Y[i] - axy[x][i];
        if(temp==0){
            visY[i] = true;
            if(match[i]==-1 || dfs(match[i],flag)){
                if(flag)    match[i] = x;
                return true;
            }
        }
        else
            slack[i] = min( slack[i] , temp );
        
        
    }
    return false;
}

long long km(){
    memset(match , -1 , sizeof(match));
    memset(ex_Y , 0 , sizeof(ex_Y));
    for(int i = 0;i<N;++i){
        ex_X[i] = axy[i][0];
        for(int j = 1;j<N;++j){
            ex_X[i] = max( ex_X[i] , axy[i][j] );
        }
    }
    
    for(int i = 0;i<N;++i){
        
        memset(visY, false, sizeof(visY));
        memset(visX, false, sizeof(visX));
        for(int j = 0;j<N;++j)  slack[j] = INF;
        
        if(dfs(i,1))    continue;
        
        bool isOK = true;
        
        while(isOK){
        
            int tempDelta = INF;
            for(int i = 0;i<N;++i){
                if(!visY[i] && tempDelta > slack[i])
                    tempDelta =  slack[i] ;
            }
            
            for(int i = 0;i<N;++i){
                if(visX[i])
                    ex_X[i] -= tempDelta;
                
                if(visY[i])
                    ex_Y[i] += tempDelta;
                else
                    slack[i] -= tempDelta;
            }
            
            for(int i = 0;i<N;++i){
                
                if(!visY[i] && slack[i]==0){
                    
                    visY[i] = true;
                    if(match[i]==-1 || dfs(match[i],0)){
                        isOK = false;
                        break;
                    }
                }
            }
            
        }
        
        memset(visY, false, sizeof(visY));
        memset(visX, false, sizeof(visX));
        dfs(i,1);
        
    }
    
    long long res = 0;
    for(int i = 0;i<N;i++)
        res += axy[match[i]][i];
    
    return res;
    
    
}

int main(){
    int T;
    scanf("%d",&T);
    int tt = T;
    while(tt--){
        
        scanf("%d",&N);
        for(int i = 0;i<N;i++){
            for(int j = 0;j<N;j++){
                scanf("%d",&axy[i][j]);
                axy[i][j]*=-1;
            }
        }
        printf("Case #%d: %lld\n",T-tt,-km());
        
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41508508/article/details/81570381