【网络流24题,最大匹配】骑士独立问题

前言

话说这题和SSL-1144一模一样,连数据范围和样例都一模一样,不同的是洛谷的只有网络流能过,匈牙利会超时。。。
于是我便在洛谷上水了篇博客,不知道过没过。。。

链接

https://www.luogu.org/problemnew/show/P3355

思路

其实这道题匈牙利也能过91分。。。但时间却是网络流的4倍。。。

匈牙利算法

奇偶建图(如果不这样会TLE更多的点),所以我们把奇数的点放在左边,偶数的点放在右边,若能攻击到彼此,则将它们连边,再求一边匈牙利

最大流求法

在匈牙利算法建模的基础上在左右两侧分别加上源点和汇点,并将源点和所有的奇数点连边,汇点和所有的偶数点连边(容量都为1)

若能攻击到彼此,则将它们连边(容量为无穷大),再跑一遍最大流即可

匈牙利算法代码

#include<cstdio>
#include<cstring>
#define N 202
#define M 20002
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;int n,m,ans,one,g[N][N],two,link[M],xq[M][2];
bool vis[M],ok[N][N];
const short dx[8]={1,1,-1,-1,2,2,-2,-2};
const short dy[8]={-2,2,-2,2,-1,1,-1,1};
int read()//输入流
{
    char c;int f=0,d=1;
    while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
    while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
bool check(int x,int y)//判断是否满足要求
{
    if(x<1||x>n||y<1||y>n) return false;
    if(ok[x][y]||vis[g[x][y]]) return false;
    return true;
}
bool find(int x)
{
    int k=0,q=0,x1,y1;
    r(i,0,7)//这里没有必要建邻接表,因为骑士顶多跳周围的八个点
    {
        x1=xq[x][0]+dx[i];
        y1=xq[x][1]+dy[i];//取下坐标
        if(check(x1,y1))//没有跳出去则进行
        {
            k=g[x1][y1];
            q=link[k];
            link[k]=x;
            vis[k]=true;
            if(!q||find(q)) return true;
            link[k]=q;
        }
    }
    return false;
}
int main()
{
    n=read();m=read();
    r(i,1,m) ok[read()][read()]=true;
    r(i,1,n)
     r(j,1,n)
      if(!ok[i][j])
       {
        if((i+j)&1)
         g[i][j]=++one;
        else
        {
            g[i][j]=++two;
            xq[two][0]=i;
            xq[two][1]=j;//奇偶建图
        }
       }
    ans=n*n-m;
    r(i,1,two)
    {
        memset(vis,0,sizeof(vis));
        if(find(i)) ans--;
    }
    printf("%d",ans);//输出
}

最大流代码

#include<cstring>
#include<cstdio>
#include<queue>
#define M 500001//这个一定要开得足够大
#define k(i,j) (i-1)*n+j//用这个表示每个点的标号
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;int id,f,n,m,d[M],l[M],s,t,sum,a;char c;
bool ok[201][201];
int read()
{
    char c;f=0;
    while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return f;
}
struct node{int next,to,w;}e[M];int tot;
const short dx[8]={1,1,-1,-1,2,2,-2,-2};
const short dy[8]={2,-2,2,-2,1,-1,1,-1};//八个方向
void add(int u,int v,int w)//建边
{
    e[tot].to=v;e[tot].w=w;e[tot].next=l[u];l[u]=tot++;
    e[tot].to=u;e[tot].w=0;e[tot].next=l[v];l[v]=tot++;
    return;
}
bool bfs()//分层图建设
{
    memset(d,-1,sizeof(d));
    queue<int>q;
    q.push(s);d[s]=0;
    while(q.size())
    {
        int x=q.front();q.pop();
        for(int i=l[x];i!=-1;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].w&&d[y]==-1)
            {
                d[y]=d[x]+1;
                q.push(y);
                if(y==t) return true;
            }
        }
    }
    return false;
}
int dfs(int x,int flow)//寻找可行流
{
    if(x==t||!flow) return flow;
    int rest=0,f;
    for(int i=l[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(d[x]+1==d[y]&&e[i].w)
        {
            rest+=(f=dfs(y,min(flow-rest,e[i].w)));
            e[i].w-=f;e[i^1].w+=f;
        }
    }
    if(!rest) d[x]=-1;
    return rest;
}
int dinic()
{
    int r=0;
    while(bfs()) r+=dfs(s,1e9);//求最大流
    return r;//返回
}
int main()
{
    memset(l,-1,sizeof(l));
    n=read();m=read();s=0;t=n*n+1;sum=n*n-m;
    while(m--) ok[read()][read()]=true;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
     {
        if(ok[i][j]) continue;//若有障碍则跳过
        if((i+j)&1)
        {
            add(s,k(i,j),1);//与源点相连
            for(int o=0;o<8;o++)
            {
                int nx=i+dx[o],ny=j+dy[o];
                if(nx<1||ny<1||nx>n||ny>n||ok[nx][ny]) continue;
                id=k(nx,ny);
                add(k(i,j),id,1e9);//能攻击到则连边,与匈牙利算法不同的是这里是奇数建边,其实无论奇数建还是偶数建都不会有什么影响
            }
        } else add(k(i,j),t,1);//连边
     }
    printf("%d",sum-dinic());//输出
}

后记

记得在学最大流的时候,老师跟我们说让我们用网络流去做最大匹配的题,当时我还有点不解,做了这道题后算是有了一个比较深入的了解吧。


网络流24题的其他题貌似也有许多可以用匈牙利算法做,不明白为什么会被评为省选/NOI-。。。

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/80765887
今日推荐