1.柯南开锁

  • 柯南决定深入OIBH组织内部, 一探虚实.他经过深思熟虑, 决定从OIBH组织大门进入...........
    OIBH组织的大门有一个很神奇的锁.锁是由M*N个格子组成, 其中某些格子凸起(灰色的格子). 每一次操作可以把某一行或某一列的格子给按下去.
    如果柯南能在组织限定的次数内将所有格子都按下去, 那么他就能够进入总部. 但是OIBH组织不是吃素的, 他们的限定次数恰是最少次数
    请您帮助柯南计算出开给定的锁所需的最少次数.

  • 这种矩阵图题目的套路就是,分为行、列两种的点集的二分图。
    对于每个需要翻转的点,我们把他的行、列连一条边,表示这条边必须被覆盖。因此我们求最小点覆盖集

代码:

    #include<stdio.h>
    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e6+5;
    int n,m;
    int nxt[N],link[N],to[N],head[N],num;
    char s[105][105];
    bool road[N];
    void add_edge(int u,int v) {
    	num++; nxt[num]=head[u]; to[num]=v; head[u]=num;
    }
    bool Find(int u) {
    	for(int i=head[u];i;i=nxt[i]) {
    		int v=to[i];
    		if(!road[v]) {
    			road[v]=true;
    			if(!link[v]||Find(link[v])) {
    				link[v]=u;
    				return true;
    			}
    		}
    	}
    	return false;
    }
    int hungarian() {
    	int tot=0;
    	for(int i=1;i<=n;i++) {
    		for(int j=1;j<=m;j++) road[j]=false;
    		if(Find(i)) tot++;
    	}
    	return tot;
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	for(int i=0;i<n;i++) {
    		scanf("%s",s[i]);
    		for(int j=0;j<m;j++) {
    			if(s[i][j]=='1') {
    				add_edge(i+1,j+1);
    			}
    		}
    	}
    	int ans=hungarian();
    	printf("%d",ans);
    	return 0;
    }

2.骑士共存问题

  • 题意
  • 思路:将棋盘黑白染色(呈国际象棋效果),白点与能到的黑点连边。为了保证不冲突,一条边上只能有最多一个点存在,跑最大点独立集
  • 代码
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=205;
const int M=1e6+5;
int dx[9]={-2,-1,-2,-1,1,2,1,2};
int dy[9]={-1,-2,1,2,-2,-1,2,1};
bool mp[N][N];
int n,m,p[N][N],Time,road[M],tot[2];
int nxt[M],link[M],to[M],head[M],num,size[M];
void add_edge(int u,int v) {
	num++; nxt[num]=head[u]; to[num]=v; head[u]=num;
}
bool Find(int u) {
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		if(road[v]!=Time) {
			road[v]=Time;
			if(!link[v]||Find(link[v])) {
				link[v]=u;
				return true;
			}
		}
	}
	return false;
}
int hungarian() {
	int sum=0;
	for(int i=1;i<=tot[1];i++) {
		Time++;
		if(Find(i)) sum++;
	}
	return sum;
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		mp[u][v]=true;
	}
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			if(!mp[i][j]) p[i][j]=++tot[(i+j)&1];
		}
	}
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			if(!mp[i][j]&&((i+j)&1)) {
				for(int d=0;d<8;d++) {
					int x=i+dx[d],y=j+dy[d];
					if(mp[x][y]||x<1||y<1||x>n||y>n) continue;
					add_edge(p[i][j],p[x][y]);
					size[p[i][j]]++;
				}
			}
		}
	}
	int cnt=hungarian();
	printf("%d\n",tot[1]+tot[0]-cnt);
	return 0;
}

未完待续......