HDU 2853 && HDU 3315

http://acm.hdu.edu.cn/showproblem.php?pid=2853

题意:给一个n-m二分图,边权用一个n*m的矩阵表示,给出初始匹配,求二分图完美匹配相比初始匹配改变了几条边以及改变的数值

这类题的主要思想是增加原配边的权值,但又不影响最后结果。

步骤1:观察顶点数,每条边乘一个大于顶点数的数v

步骤2:对于原配边,每边加1(注意步骤2可以保证km()/v的结果与原结果相同)

步骤3:求完美匹配,答案为res,改变的边数=n-res%v(res%v表示完美匹配中有多少边属于原匹配),要求的完美匹配答案=res/v

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=310;
const int INF=0x3f3f3f3f;
int nx,ny;
int linker[N],lx[N],ly[N],slack[N];
int visx[N],visy[N],w[N][N];
int DFS(int x)
{
    visx[x]=1;
    for(int y=1;y<=ny;y++){
        if(visy[y])
            continue;
        int tmp=lx[x]+ly[y]-w[x][y];
        if(tmp==0){
            visy[y]=1;
            if(linker[y]==-1 || DFS(linker[y])){
                linker[y]=x;
                return 1;
            }
        }else if(slack[y]>tmp){
            slack[y]=tmp;
        }
    }
    return 0;
}
int change ;
int KM()
{
    int i,j;
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(i=1;i<=nx;i++)
        for(j=1,lx[i]=-INF;j<=ny;j++)
            if(w[i][j]>lx[i])
                lx[i]=w[i][j];
    for(int x=1;x<=nx;x++){
        for(i=1;i<=ny;i++)
            slack[i]=INF;
        while(1){
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(DFS(x))
                break;
            int d=INF;
            for(i=1;i<=ny;i++)
                if(!visy[i] && d>slack[i])
                    d=slack[i];
            for(i=1;i<=nx;i++)
                if(visx[i])
                    lx[i]-=d;
            for(i=1;i<=ny;i++)
                if(visy[i])
                    ly[i]+=d;
                else
                    slack[i]-=d;
        }
    }
    int res=0;
    for(i=1;i<=ny;i++)
        if(linker[i]!=-1)
            res+=w[linker[i]][i];
    change=nx-res%200 ;
    return res/200;
}
int main()
{
    while(~scanf("%d%d",&nx,&ny))
    {
        for(int i=1;i<=nx;i++)
            for(int j=1;j<=ny;j++)
            {
                scanf("%d",&w[i][j]);
                w[i][j]*=200 ;
            }
        int res=0 ;
        for(int i=1 ;i<=nx ;i++)
        {
            int x ;
            scanf("%d",&x) ;
            res+=w[i][x] ;
            w[i][x]++ ;
        }
        int ans=KM();
        printf("%d %d\n",change,ans-res/200);
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=3315

与2853一样思路的题目,区别是这题图没有直接给,要先算一下

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=310;
const int INF=0x3f3f3f3f;
int n,nx,ny;
int linker[N],lx[N],ly[N],slack[N];
int visx[N],visy[N],w[N][N];
int DFS(int x)
{
    visx[x]=1;
    for(int y=1;y<=ny;y++){
        if(visy[y])
            continue;
        int tmp=lx[x]+ly[y]-w[x][y];
        if(tmp==0){
            visy[y]=1;
            if(linker[y]==-1 || DFS(linker[y])){
                linker[y]=x;
                return 1;
            }
        }else if(slack[y]>tmp){
            slack[y]=tmp;
        }
    }
    return 0;
}
int change ;
int KM()
{
    int i,j;
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(i=1;i<=nx;i++)
        for(j=1,lx[i]=-INF;j<=ny;j++)
            if(w[i][j]>lx[i])
                lx[i]=w[i][j];
    for(int x=1;x<=nx;x++){
        for(i=1;i<=ny;i++)
            slack[i]=INF;
        while(1){
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(DFS(x))
                break;
            int d=INF;
            for(i=1;i<=ny;i++)
                if(!visy[i] && d>slack[i])
                    d=slack[i];
            for(i=1;i<=nx;i++)
                if(visx[i])
                    lx[i]-=d;
            for(i=1;i<=ny;i++)
                if(visy[i])
                    ly[i]+=d;
                else
                    slack[i]-=d;
        }
    }
    int res=0;
    for(i=1;i<=ny;i++)
        if(linker[i]!=-1)
            res+=w[linker[i]][i];
    if(res<=0)return -1 ;
    change=nx-res%200 ;
    return res/200;
}
int V[N],H1[N],A1[N],H2[N],A2[N] ;
int main()
{
    while(~scanf("%d",&n),n)
    {
        nx=ny=n ;
        for(int i=1 ;i<=n ;i++)
            scanf("%d",&V[i]) ;
           for(int i=1 ;i<=n ;i++)
               scanf("%d",&H1[i]) ;
        for(int i=1 ;i<=n ;i++)
               scanf("%d",&H2[i]) ;
        for(int i=1 ;i<=n ;i++)
               scanf("%d",&A1[i]) ;
        for(int i=1 ;i<=n ;i++)
               scanf("%d",&A2[i]) ;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                int x ;
                x=H1[i]/A2[j] ;
                if(H1[i]%A2[j])x++ ;
                if(x*A1[i]>=H2[j])w[i][j]=V[i]*200 ;
                else w[i][j]=(-V[i]*200) ;
            }
        for(int i=1 ;i<=n ;i++)
            w[i][i]++ ;
        int ans=KM();
        if(ans==-1)puts("Oh, I lose my dear seaco!") ;
        else printf("%d %.3lf%%\n",ans,(nx-change)*100.0/n);
    }
    return 0;
}

HDU 2853 && HDU 3315

时间: 2024-10-10 02:14:47

HDU 2853 && HDU 3315的相关文章

hdu 2853 Assignment 费用流

就是本来就给出了一个匹配,然后让你求一个权值最大的匹配,并且和初始匹配变动最小. #include <stdio.h> #include <iostream> #include <string.h> using namespace std; const int N=400; const int MAXE=20000000; const int inf=1<<30; int head[N],s,t,cnt,ans; int d[N],pre[N]; bool

HDU 2853 Assignment(KM最大匹配好题)

HDU 2853 Assignment 题目链接 题意:现在有N个部队和M个任务(M>=N),每个部队完成每个任务有一点的效率,效率越高越好.但是部队已经安排了一定的计划,这时需要我们尽量用最小的变动,使得所有部队效率之和最大.求最小变动的数目和变动后和变动前效率之差. 思路:对于如何保证改变最小,没思路,看了别人题解,恍然大悟,表示想法非常机智 试想,如果能让原来那些匹配边,比其他匹配出来总和相同的权值还大,对结果又不影响,那就简单了,这个看似不能做到,其实是可以做到的 数字最多选出50个,所

hdu 3061 hdu 3996 最大权闭合图 最后一斩

hdu 3061 Battle :一看就是明显的最大权闭合图了,水提......SB题也不说边数多少....因为开始时候数组开小了,WA....后来一气之下,开到100W,A了.. hdu3996.  gold mine..看了一下,简单题,几乎裸,不敲了.. #include<iostream>//Battle #include<queue> #include<cstdio> #include<cstring> #include<set> #i

hdu 3081 hdu 3277 hdu 3416 Marriage Match II III IV //最大流的灵活运用

3081 题意: n个女孩选择没有与自己吵过架的男孩有连边(自己的朋友也算,并查集处理),2分图,有些边,求有几种完美匹配(每次匹配每个点都不重复匹配) 我是建二分图后,每次增广一单位,(一次完美匹配),再修改起点还有终点的边流量,继续增广,直到达不到完美匹配为止.网上很多是用二分做的,我觉得没必要...(网上传播跟风真严重...很多人都不是真正懂最大流算法的...) 3277 : 再附加一条件,每个女孩可以最多与k个自己不喜欢的男孩.求有几种完美匹配(同上). 我觉得:求出上题答案,直接ans

2014多校联合六(HDU 4923 HDU 4925 HDU 4927 HDU 4930)

HDU 4923 Room and Moor 题意:给出A序列  求满足题目所写的B序列  使得方差最小 思路:可以想到最后的结果中  B序列的值一定是一段一段的  那么我们可以类似贪心去搞  对于一段序列我们可以求出什么样的b值使得方差最小  即序列中1的个数除以序列长度  又因为B是单调的  可以用一个单调栈去模拟  复杂度远远小于n^2  不要被吓怕- 代码: #include<cstdio> #include<cstring> #include<algorithm&g

2014多校联合三 (HDU 4888 HDU 4891 HDU 4893)

HDU 4891 The Great Pan 签到题  他怎么说你就怎么做就好了  注意做乘法时候会爆int 代码: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; int n; char s[2000000]; int flag1, flag2, sp, ansflag;

SG 函数初步 HDU 1536 &amp;&amp; HDU 1944

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1944 http://acm.hdu.edu.cn/showproblem.php?pid=1536 给定每一次可以取的石头数,给定很多种情况,每一种情况有若干堆石头,判断先手胜负. SG函数打表,然后直接抑或,判断结果是否为0,第一次写SG函数,贴个代码,慢慢理解. 代码: /* *********************************************** Author :rabb

hdu 3879 hdu 3917 构造最大权闭合图 俩经典题

hdu3879  base station : 各一个无向图,点的权是负的,边的权是正的.自己建一个子图,使得获利最大. 一看,就感觉按最大密度子图的构想:选了边那么连接的俩端点必需选,于是就以边做点,轻轻松松构造了最大权闭合图.简单题.分分钟搞定. hdu3917 :road  constructions :这题题目看了半天没理解...感觉描述的不好...一个有向图,每条路有响应公司承保,若选了该公司,那么该公司的路必需全部选,还有,该公司的承保的路的下面的一条路对应公司也要选,求最大获利.构

2014多校联合五(HDU 4911 HDU 4915 HDU 4920)

HDU 4911 Inversion 题意:n个数字  通过k次相邻交换  使得逆序对数最少 思路:如果序列为 XXXABYYY  假设A和B位置互换  易知X和AB.Y和AB的逆序对数不变  换句话说一次交换最多使逆序对减少1  那么只需要求原逆序对数和k进行比较即可 代码: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100100 type