题目链接:骑士共存问题


贴一个很相似的题吧:方格取数

这两个题:如果放在了一起,相信应该都会有思路的吧!

黑白染色法:求得最小割,然后用所有的可行点减去最小割就是:最大独立集


然后呢,建图有两种方法的

第一种方法:不拆点:那么我们需要对每个节点黑白染色:注意!

这个染色与方格取数不一样了:因为有些点是障碍,那么黑白点的染色方案就变了

在计算的时候,只有非障碍点才编号

然后在连边的时候

源点S向所有的(i+j)%2==1的连边

所有的(i+j)%2==0向汇点T连边

然后,按照骑士的规则

枚举所有的可能情况就好


int main(){
    //freopen("input.txt","r",stdin);
    int m,x,y,c;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(mp,0,sizeof(mp));
        c=s=0;t=n*n+1;tot=t+1;
        init();
        //for(int i=1;i<=n;i++)
          //  for(int j=1;j<=n;j++)
            //    printf("%d%c",mp[i][j],j==n?'\n':' ');
        int ans=n*n-m;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            mp[x][y]=-1;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if (mp[i][j]!=-1)
                    mp[i][j]=++c;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if (mp[i][j]!=-1){
                    //(i*j)&1==1
                    //i和j都是奇数
                    //!(i&1)说明i是偶数
                    //!(j&1)说明j是偶数
                //if((i*j)&1 || ((!(i&1))&&(!(j&1)))){
                if ((i+j)%2==0){
                    addedge(s,mp[i][j],1);
                    for(int k=0;k<8;k++){
                        int nx=i+dx[k];
                        int ny=j+dy[k];
                        if (nx>=1&&nx<=n&&ny>=1&&ny<=n&&mp[nx][ny]!=-1)
                            addedge(mp[i][j],mp[nx][ny],1);
                    }
                }
                else addedge(mp[i][j],t,1);
            }
        }
        int mflow;
        //mflow=dinic(s,t,tot);
        mflow=sap(s,t,tot);
        //printf("%d\n",mflow);
        printf("%d\n",ans-mflow);
    }
    return 0;
}

方法2:拆点

拆点就不需要考虑这么多了

把X集合和Y集合全部分开想

但是求出的最小割是原来的2倍

因为每条边都有两条啦,可以画画图就懂细节了


#include<bits/stdc++.h>
using namespace std;

const int maxn=105000;
const int maxm=1000050;
const int maxl=250;
const int INF=0x3f3f3f3f;
int s,t,tot,n;
int mp[maxl][maxl];

int dx[]={2,2,1,1,-1,-1,-2,-2};
int dy[]={1,-1,2,-2,2,-2,1,-1};

struct Edge{
    int to,nxt,cap,flow;
}edge[maxm];
int tol,Head[maxn];

void init(){
    memset(Head,-1,sizeof(Head));
    tol=0;
}

void addedge(int u,int v,int w,int rw=0){
    edge[tol].to=v;
    edge[tol].cap=w;
    edge[tol].flow=0;
    edge[tol].nxt=Head[u];
    Head[u]=tol++;
    edge[tol].to=u;
    edge[tol].cap=rw;
    edge[tol].flow=0;
    edge[tol].nxt=Head[v];
    Head[v]=tol++;
}

int Q[maxn],dep[maxn],cur[maxn],pre[maxn];
int gap[maxn];

void BFS(int Start,int End){
    memset(dep,-1,sizeof(dep));
    memset(gap,0,sizeof(gap));
    gap[0]=1;
    int Front=0,Rear=0;
    dep[End]=0;
    Q[Rear++]=End;
    while(Front!=Rear){
        int u=Q[Front++];
        for(int i=Head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if (dep[v]!=-1) continue;
            Q[Rear++]=v;
            dep[v]=dep[u]+1;
            gap[dep[v]]++;
        }
    }
}

int S[maxn];
int sap(int Start,int End,int N){
    BFS(Start,End);
    memcpy(cur,Head,sizeof(Head));
    int top=0;
    int u=Start;
    int ans=0;
    while(dep[Start]<N){
        if (u==End){
            int Min=INF;
            int inser;
            for(int i=0;i<top;i++)
            if (Min>edge[S[i]].cap-edge[S[i]].flow){
                Min=edge[S[i]].cap-edge[S[i]].flow;
                inser=i;
            }
            for(int i=0;i<top;i++){
                edge[S[i]].flow+=Min;
                edge[S[i]^1].flow-=Min;
            }
            ans+=Min;
            top=inser;
            u=edge[S[top]^1].to;
            continue;
        }
        bool flag=false;
        int v;
        for(int i=cur[u];i!=-1;i=edge[i].nxt){
            v=edge[i].to;
            if (edge[i].cap-edge[i].flow&&dep[v]+1==dep[u]){
                flag=true;
                cur[u]=i;
                break;
            }
        }
        if (flag){
            S[top++]=cur[u];
            u=v;
            continue;
        }
        int Min=N;
        for(int i=Head[u];i!=-1;i=edge[i].nxt)
        if (edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min){
            Min=dep[edge[i].to];
            cur[u]=i;
        }
        gap[dep[u]]--;
        if (!gap[dep[u]]) return ans;
        dep[u]=Min+1;
        gap[dep[u]]++;
        if (u!=Start) u=edge[S[--top]^1].to;
    }
    return ans;
}

/*
int sap(int Start,int End,int N){
    memset(gap,0,sizeof(gap));
    memset(dep,0,sizeof(dep));
    memcpy(cur,Head,sizeof(Head));
    int u=Start;
    pre[u]=-1;
    gap[0]=N;
    int ans=0;
    while(dep[Start]<N){
        if (u==End){
            int Min=INF;
            for(int i=pre[u];i!=-1;i=pre[edge[i^1].to])
                if (Min>edge[i].cap-edge[i].flow)
                    Min=edge[i].cap-edge[i].flow;
            for(int i=pre[u];i!=-1;i=pre[edge[i^1].to]){
                edge[i].flow+=Min;
                edge[i^1].flow-=Min;
            }
            u=Start;
            ans+=Min;
            continue;
        }
        bool flag=false;
        int v;
        for(int i=cur[u];i!=-1;i=edge[i].nxt){
            v=edge[i].to;
            if (edge[i].cap-edge[i].flow&&dep[v]+1==dep[u]){
                flag=true;
                cur[u]=pre[v]=i;
                break;
            }
        }
        if (flag){
            u=v;
            continue;
        }
        int Min=N;
        for(int i=Head[u];i!=-1;i=edge[i].nxt)
        if (edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min){
            Min=dep[edge[i].to];
            cur[u]=i;
        }
        gap[dep[u]]--;
        if (!gap[dep[u]]) return ans;
        dep[u]=Min+1;
        gap[dep[u]]++;
        if (u!=Start) u=edge[pre[u]^1].to;
    }
    return ans;
}
*/
int getpoint(int x,int y){
    return (x-1)*n+y;
}

int main(){
    //freopen("input.txt","r",stdin);
    int m,x,y;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(mp,0,sizeof(mp));
        s=0;t=2*n*n+1;tot=t+1;
        init();
        int ans=n*n-m;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            mp[x][y]=1;
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if (!mp[i][j]){
                addedge(s,getpoint(i,j),1);
                addedge(getpoint(i,j)+n*n,t,1);
            }
            for(int k=0;k<8;k++){
                int nx=i+dx[k];
                int ny=j+dy[k];
                if (nx>=1&&nx<=n&&ny>=1&&ny<=n&&!mp[nx][ny])
                    addedge(getpoint(i,j),getpoint(nx,ny)+n*n,1);
            }
        }
        int mflow;
        //mflow=dinic(s,t,tot);
        mflow=sap(s,t,tot);
        printf("%d\n",ans-mflow/2);
    }
    return 0;
}

这个题最坑的是细节!

n=200,在第一种方法里,建图中的点可能有200*200=40000个点

而拆点之后则是80000个啊

初始化的数据大小一定要经过计算的!