• 棋盘游戏 (匈牙利算法,最大匹配)

VJ链接:https://vjudge.net/problem/HDU-1281
由题目描述中的“不在这些格子上放车,也可以保证尽量多的“车”被放下。
可以联想到这句话本意即为这条路存在于在增广路中,属于“翻转”增广路之前存在的匹配
(图示增广路中箭头所指就是一条“非重要边‘)(土丑土丑的手绘。)
想到了将行与列做最大匹配(一个车攻击范围为该行和该列嘛),但是没想到怎么判断“重要边”。
后来发现直接删边判断最大匹配有没有变小就可以了。。。。
匈牙利算法复杂度O(n*m),每个点都需要去寻找一次匹配,匹配过程最坏的情况就是全部边都要变,所以为n * m。
总复杂度为O((n*m)^2)(第一次尝试计算复杂度也不知道对没。。不会算复杂度还挺麻烦的。。)
代码:
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<stack>
#include<ctime>
#include<cstdio>
#include<vector>
#include<string>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e6+10;
#define pi  acos(-1.0)
#define INF  0x3f3f3f3f
#define mod   1000000009
#define endll printf("\n")
#define s1(a) scanf("%d",&a)
#define s2(a,b) scanf("%d %d",&a,&b)
#define mem(a,b) memset(a,b,sizeof(a))
#define s3(a,b,c) scanf("%d %d %d",&a,&b,&c)

int n,m,k;
int mp[111][111];
int vis[111],pei[111];

int road[MAXN<<1];
int dfs(int i)
{
    for(int j=1;j<=m;j++)
    {
        if(mp[i][j]&&vis[j]==0)
        {
            vis[j]=1;
            if(!pei[j]||dfs(pei[j]))
            {
                pei[j]=i;
                return 1;
            }
        }
    }
    return 0;
}
int hungry()//
{
    mem(pei,0);
    int ans=0;
    //求出原图的最大匹配
    for(int i=1;i<=n;i++)
    {
        mem(vis,0);
        ans+=dfs(i);
    }
    return ans;
}
int main()
{
    int t=1;
    while(~s3(n,m,k))
    {
        mem(mp,0);
        for(int i=1,a,b;i<=k;i++)
        {
            s2(a,b);
            mp[a][b]=1;
            road[2*i-1]=a;
            road[2*i]=b;
        }
        //
        int ans=hungry();
        //
        int sum=0;
        for(int i=1;i<=2*k;i+=2)
        {
            mp[road[i]][road[i+1]]=0;
            int temp=hungry();
            if(temp!=ans)
                sum++;
            mp[road[i]][road[i+1]]=1;
        }
        printf("Board %d have %d important blanks for %d chessmen.\n",t++,sum,ans);
    }
    return 0;
}
  • Swap

VJ链接:https://vjudge.net/problem/HDU-2819
问能不能通过交换,使主对角线全为一,可以发现如果满足题目条件,行和列存在一一对应关系,
和上一题相似,棋盘中某格为1表示该行与该列可以匹配。
做一次最大匹配看等不等n,输出路径方面,匈牙利算法的match数组已经记录了路径。‘
手动模拟交换过程即可(第一次遇见要输出路径的,终于理顺了点怎么输出,,,),比如,
要达到交换某两行的目的,只要交换match数组即可,如此样例中,
对于原棋盘,第1列要去第4列,第3列要去第1列,
交换1,3列之后,原本的第3列变成了第1列,那么此时棋盘中第1列不必交换(第一列匹配成功),
原本的第1列变成了第3列,此时他还是要去第4列,那么此时match[3](=4)应为原本的match[1](=4)(交换后的第三列即为原本的第一列)
match[1]同理。
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<stack>
#include<ctime>
#include<cstdio>
#include<vector>
#include<string>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e6+10;
#define pi  acos(-1.0)
#define INF  0x3f3f3f3f
#define mod   1000000009
#define endll printf("\n")
#define s1(a) scanf("%d",&a)
#define s2(a,b) scanf("%d %d",&a,&b)
#define mem(a,b) memset(a,b,sizeof(a))
#define s3(a,b,c) scanf("%d %d %d",&a,&b,&c)
int n;
int mp[111][111];
int vis[111],pei[111];
int dfs(int i)
{
    for(int j=1; j<=n; j++)
    {
        if(mp[i][j]&&vis[j]==0)
        {
            vis[j]=1;
            if(!pei[j]||dfs(pei[j]))
            {
                pei[j]=i;
                return 1;
            }
        }
    }
    return 0;
}
int hungry()
{
    mem(pei,0);
    int ans=0;
    for(int i=1; i<=n; i++)
        mem(vis,0),ans+=dfs(i);
    return ans;
}
int main()
{
    while(~s1(n))
    {
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                s1(mp[i][j]);
        int ans=hungry();
        if(ans!=n) printf("-1\n");
        else
        {
//            for(int k=1; k<=n; k++)
//                printf("pei[%d]=%d\n",k,pei[k]);
            int num = 0,a[110], b[110];
            for (int i = 1; i <= n; i++)
            {
                if (i == pei[i]) continue;
                for (int j = 1; j <= n; j++)
                {
                    if (i ==pei[j])
                    {
                        a[num]=i;b[num++]=j;
                        swap(pei[i],pei[j]);
//                            printf("after swap %d and %d:\n",i,j);
                        break;
                    }
                }
//                for(int k=1; k<=n; k++)
//                    printf("pei[%d]=%d\n",k,pei[k]);
            }
            printf("%d\n",num);
            for (int i=0;i<num;i++)
                printf("C %d %d\n",a[i],b[i]);
        }
    }
    return 0;
}
  • Rain on your Parade 

VJ链接:https://vjudge.net/problem/HDU-2389
建图,OK,匈牙利,OK,提交,T。
匈牙利算法复杂度n*m,这里n是3000,m最大3000*3000,n*m的时间就炸掉了。。。
第一次知道还有HK算法,大概看了一下原理,以后补HK,先抄板(危险发言)。