KM匹配 hdu2853(模版

来源:互联网 发布:家具有味怎么办知乎 编辑:程序博客网 时间:2024/05/29 08:32

传送门



Assignment

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1418    Accepted Submission(s): 744


Problem Description
Last year a terrible earthquake attacked Sichuan province. About 300,000 PLA soldiers attended the rescue, also ALPCs. Our mission is to solve difficulty problems to optimization the assignment of troops. The assignment is measure by efficiency, which is an integer, and the larger the better.
We have N companies of troops and M missions, M>=N. One company can get only one mission. One mission can be assigned to only one company. If company i takes mission j, we can get efficiency Eij. 
We have a assignment plan already, and now we want to change some companies’ missions to make the total efficiency larger. And also we want to change as less companies as possible.
 

Input
For each test case, the first line contains two numbers N and M. N lines follow. Each contains M integers, representing Eij. The next line contains N integers. The first one represents the mission number that company 1 takes, and so on.
1<=N<=M<=50, 1<Eij<=10000.
Your program should process to the end of file.
 

Output
For each the case print two integers X and Y. X represents the number of companies whose mission had been changed. Y represents the maximum total efficiency can be increased after changing.
 

Sample Input
3 32 1 33 2 41 26 22 1 32 31 2 31 2 31 2
 

Sample Output
2 261 2
 

Source
2009 Multi-University Training Contest 5 - Host by NUDT
 

Recommend
gaojie

题意:给定一个二分图,N个点对应M个点,两两之间存在一组关系,每组关系一个权值。题目中了给定了一个匹配方案,现在要求满足这组关系中的最大的匹配权值在原方案上增长了多少?并且还要求求出在原匹配方案上改变最少多少条边能够得到这个最大匹配?

idea:增加原配边的权值,而且又不会对结果造成影响。这听起来似乎是不太可能的,但是确实有办法能够办到。首先由于顶点数最多50个,那么给所有的边都乘上55,然后再给所有的原配边都加上1,那么此时的原配边的权值相比其他边更大了,又因为我们最后求解最大权值匹配时是对结果除上55,原配边匹配再多的数量对这个结果也是于事无补,所有最后MaxValue / 55就是匹配的最大权值,而MaxValue % 55就是能够保持的最大原配边数。转自此处

#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define pi acos(-1)#define endl '\n'#define srand() srand(time(0));#define me(x,y) memset(x,y,sizeof(x));#define foreach(it,a) for(__typeof((a).begin()) it=(a).begin();it!=(a).end();it++)#define close() ios::sync_with_stdio(0); cin.tie(0);#define FOR(x,n,i) for(int i=x;i<=n;i++)#define FOr(x,n,i) for(int i=x;i<n;i++)#define W while#define sgn(x) ((x) < 0 ? -1 : (x) > 0)#define bug printf("***********\n");typedef long long LL;const int INF=0x3f3f3f3f;const LL LINF=0x3f3f3f3f3f3f3f3fLL;const int dx[]={-1,0,1,0,1,-1,-1,1};const int dy[]={0,1,0,-1,-1,1,-1,1};const int maxn=1e3+100;const int maxx=1e7+100;const double EPS=1e-7;const int MOD=10000007;#define mod(x) ((x)%MOD);template<class T>inline T min(T a,T b,T c) { return min(min(a,b),c);}template<class T>inline T max(T a,T b,T c) { return max(max(a,b),c);}template<class T>inline T min(T a,T b,T c,T d) { return min(min(a,b),min(c,d));}template<class T>inline T max(T a,T b,T c,T d) { return max(max(a,b),max(c,d));}inline int Scan(){    int Res=0,ch,Flag=0;    if((ch=getchar())=='-')Flag=1;    else if(ch>='0' && ch<='9')Res=ch-'0';    while((ch=getchar())>='0'&&ch<='9')Res=Res*10+ch-'0';    return Flag ? -Res : Res;}//freopen( "in.txt" , "r" , stdin );//freopen( "data.out" , "w" , stdout );//cerr << "run time is " << clock() << endl;int g[maxn][maxn];     //二分图表示.int link[maxn],lx[maxn],ly[maxn];   //y的匹配关系. x,y的标杆状态.bool visx[maxn],visy[maxn];int nx,ny,d;   //两边的点数. 以及需要寻找的最小的d.bool Find(int x){    visx[x] = true;    for(int i=1;i<=ny;i++)    {        if(visy[i]) continue;        int tmp = lx[x] + ly[i] - g[x][i];        if(!tmp)        {   //tmp=0表示在当前图匹配了的边, 即看现在匹配是否合理,            visy[i] = true;            if(link[i] == -1 || Find(link[i]))            {                link[i] = x;                return true;            }        }        else d=min(d,tmp); //就是在S集合中的x,和不在T集合中的y找一个最小的d.    }    return false;}int KM(){    me(link,-1);    me(ly,0); me(lx,0);    for(int i=1;i<=nx;i++)    {        for(int j=1;j<=ny;j++)        {            if(g[i][j] > lx[i])                lx[i] = g[i][j];        }    }    for(int i =1 ; i<= nx;i++)    {        while(1)        {            me(visx,false);            me(visy,false);            d = INF;            if(Find(i)) break;            //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广            //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。            //方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,            //所有在增广轨中的Y方点的标号全部加上一个常数d            if(d == INF) return -1;    //如果可以确定左边的点都会被匹配完,则就可以不用加这条语 //句.    如果不能就要加上这句话. (所以一般在题目中给了一定完美匹配的话,就可以不用这句话) //而有些题目会问你是否是完美匹配,不是的话输出-1,是的话在输出答案,所以这个时候就要加上这句话. //大多数题目是可以不用加的.            for(int j = 1 ; j<=nx ; j++)                if(visx[j]) lx[j] -= d;            for(int j = 1 ; j<=ny ; j++)                if(visy[j]) ly[j] += d;        }    }    int res = 0;    for(int i=1;i<=ny;i++)    {        if(link[i] != -1)   //这些都根据题意来加.            res += g[link[i]][i];    }    cout<<nx-res%55<<" ";    return res;}    //这道题是保证了一定有最大二分匹配的!!! 所以一些东西可以去掉.void solve(){    int n,m;    while(~scanf("%d%d",&n,&m))    {        me(g,0);   //找最大所以初始化为较小的值. 这样一减就会很小了.        for(int i=1;i<=n;i++)            for(int j=1;j<=m;j++)            {                scanf("%d",&g[i][j]);                g[i][j]*=55;            }        int res1=0;        nx=n;ny=m;        for(int i=1;i<=nx;i++)        {            int u;            scanf("%d",&u);            res1+=g[i][u];            g[i][u]++;        }        int res = KM();        printf("%d\n",(res-res1)/55);    }}int main(){    solve();}