【网络流24题】骑士共存问题(二分图染色+最大权独立子集+最小割)

8 篇文章 0 订阅
3 篇文章 0 订阅
博客探讨了骑士共存问题,将其转化为最大权独立子集与最小割问题。通过建立二分图并进行染色,寻找连接S和T的最小割以获取最大解决方案。
摘要由CSDN通过智能技术生成

传送门

    骑士共存问题

I think

    同方格取数相同,是最大独立集问题.连边之后,找到一个割,此时与S和T仍然连接的点必然不在一个集合中,点的数量就是答案.要答案尽量大,于是就找出最小割即可.至于如何染色,应当从题给的图中得到启发。

Code

#include<cstdio>
#include<queue>
using namespace std;

const int sm = 4e4+10;
const int sn = 51e4;
const int Inf = 0x3f3f3f3f;

int N,K,S,T,tot=1;
int to[sn],hd[sm],nxt[sn],c[sn];
int cur[sm],lev[sm];
bool no[205][205];

struct node {
    int x,y,g;
}now;

int xx[8] = {2,2,-2,-2,1,-1,1,-1};
int yy[8] = {1,-1,1,-1,2,2,-2,-2};

int Min(int x,int y) { return x<y?x:y; }
int Point(int x,int y) { return (x-1)*N+y; }
void Add(int u,int v,int w) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=w;
    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=0;
}
bool chk(int x) { return x>=1&&x<=N; }
void Tag() {
    for(int i=1;i<=N;++i)
        for(int j=1;j<=N;++j) {
            if(no[i][j]) continue;
            if(i%2==j%2) {
                Add(S,Point(i,j),1);
                for(int k=0;k<8;++k)
                    if(chk(i+xx[k])&&chk(j+yy[k])&&!no[i+xx[k]][j+yy[k]])
                        Add(Point(i,j),Point(i+xx[k],j+yy[k]),Inf);
            }
            else Add(Point(i,j),T,1);
        } 
}

bool Bfs() {
    for(int i=1;i<=T;++i)lev[i]=0;
    queue<int>q; int t;
    q.push(S),lev[S]=1;
    while(!q.empty()) {
        t=q.front(),q.pop();
        for(int i=hd[t];i;i=nxt[i])
            if(!lev[to[i]]&&c[i]>0) {
                lev[to[i]]=lev[t]+1;
                if(to[i]==T) return 1;
                q.push(to[i]);
            }
    }
    return 0;
}
int Dfs(int x,int mx) {
    if(x==T||!mx) return mx;
    int f;
    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
        cur[x]=i;
        if(lev[to[i]]==lev[x]+1&&c[i]>0) 
            if(f=Dfs(to[i],Min(mx,c[i])))
                return c[i]-=f,c[i^1]+=f,f;
    }
    return 0;
}
void Dinic() {
    int Flw=0,f;
    while(Bfs()) {
        for(int i=1;i<=T;++i)cur[i]=0;
        while(f=Dfs(S,Inf)) Flw+=f; 
    }
    printf("%d\n",N*N-K-Flw);
}
int main() {
    int u,v;
    scanf("%d%d",&N,&K);
    S=N*N+1,T=S+1;
    for(int i=1;i<=K;++i) {
        scanf("%d%d",&u,&v);
        no[u][v]=1;
    }
    Tag();
    Dinic();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值