P3355 骑士共存问题【洛谷】(二分图最大独立集变形题) //链接矩阵存图

 展开

题目描述

在一个 n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示。棋盘上某些方格设置了障碍,骑士不得进入

对于给定的 n*n 个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑士,使得它们彼此互不攻击

输入格式

第一行有 2 个正整数n 和 m (1<=n<=200, 0<=m<n2),分别表示棋盘的大小和障碍数。接下来的 m 行给出障碍的位置。每行 2 个正整数,表示障碍的方格坐标。

输出格式

将计算出的共存骑士数输出

输入输出样例

输入
3 2
1 1
3 3

 输出:

5

思路:本题显然不存在唯一一行对应唯一一列(車的放置)的关系,那么我们将每个“日”字的对角线进行连接 表示这两个端点无法共存。原题转化为:如何去掉最少的点,去掉所有的边

等效于点数-最小点覆盖。那么引出:最大独立集=n-最小点覆盖

类似于【Asteroids POJ - 3041 【最小点覆盖集】】的思想。先求出除了障碍物以外每一个点所能到达的点【到达的点不能是障碍物】。然后在两个相互能到达的点集中 求最小点覆盖{即删掉最小的点使得删掉所有的边【套模板】 }(求出的答案  注意 ans/2   因为是相互的)。然后再套公式:最大独立集=n-最小点覆盖即可。

AC代码:

 1 #include<bits/stdc++.h>
 2 
 3 //最大独立集=n-最小点覆盖
 4 using namespace std;
 5 #define maxn 666
 6 int dx[]={1,1,2,2,-1,-1,-2,-2};
 7 int dy[]={2,-2,1,-1,2,-2,1,-1};
 8 int mp[maxn][maxn];
 9 int match[666*666];
10 int vis[666*666];
11 vector<int> v[666*666*3];
12 int num[maxn][maxn];
13 int flag=0;
14 int n,m;
15 int head[maxn*maxn];
16 inline int read(){
17     char c = getchar(); int x = 0, f = 1;
18     while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
19     while(c >= '0' & c <= '9') x = x * 10 + c - '0', c = getchar();
20     return x * f;
21 }
22 struct Edge{
23     int to,next;
24 }e[666*666*4];
25 void  add(int u,int v){
26     flag++;
27     e[flag].to=v;
28     e[flag].next=head[u];
29     head[u]=flag;
30 } 
31 int dfs(int u){
32     for(int i=head[u];i;i=e[i].next){
33         int temp=e[i].to;
34         if(!vis[temp]){
35             vis[temp]=1;
36             if(match[temp]==0||dfs(match[temp]))
37             {
38                 match[temp]=u;
39                 return 1;
40             }
41         }
42     }
43     return 0;
44 } 
45 void ok(int x,int y){
46     for(int i=0;i<8;i++){
47         int tx=x+dx[i];
48         int ty=y+dy[i];
49         if(tx>0&&ty>0&&tx<=n&&ty<=n&&!mp[tx][ty]){
50             //v[num[x][y]].push_back(num[tx][ty]);
51             //v[num[tx][ty]].push_back(num[x][y]);
52             add(num[x][y],num[tx][ty]);
53             add(num[tx][ty],num[x][y]);
54         }
55     }
56 }
57 int main(){
58     //int n,m;
59     //scanf("%d%d",&n,&m);
60     n=read();
61     m=read();
62     for(int i=1;i<=m;i++){
63         int x,y;
64         //scanf("%d%d",&x,&y);
65         x=read();y=read();
66         mp[x][y]=1;// 标记不可以走到的点 
67     }
68     int cnt=0;
69     for(int i=1;i<=n;i++)
70         for(int j=1;j<=n;j++)
71             num[i][j]=++cnt;        // 给每一个点编号 
72     for(int i=1;i<=n;i++){
73         for(int j=1;j<=n;j++){
74             if(mp[i][j])
75                 continue;
76             else{
77                 ok(i,j);
78             }
79         }
80     }
81     //memset(match,0,sizeof(match));
82     int ans=0;
83     for(int i=1;i<=cnt;i++){
84     //    memset(vis,0,sizeof(vis));
85         for(int i=0;i<=cnt;i++)
86             vis[i]=0;
87         if(dfs(i))
88             ans++;
89     }
90     int res=n*n-m-ans/2;
91     printf("%d
",res);
92     return 0;
93 } 
原文地址:https://www.cnblogs.com/pengge666/p/11626148.html