大意:
对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的互相攻击。
所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么?
思路:
1、首先我们考虑以X,Y轴为节点连边,这样的话,就可以保证一个“车”可以统治它所在的列和行了。为什么可以这样呢?因为由二分图的性质可知,没有两条边是依附于一个顶点,自己画画图就可以知道以X,Y轴为节点连边刚好满足条件。
2、求得二分匹配之后我们怎样操作?根据关键点的定义我们可以想到,再求第一次二分匹配之后,我们将已经匹配的点存起来,然后我们通过枚举的方式将这些边删除之后求二分匹配,这样我们就可以得到以前的点是不是关键点了。
3、注意这里数据范围是100*100,我想了想,如果数据范围还大一些的话,用邻接表存储是十分麻烦的,因为删除边的操作比较麻烦,于是我用邻接矩阵去实现的。
另外:一个小错误导致我想了一天多的时间才写出来。
CODE:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAXN 110
#define MAXM 40010
struct Edge
{
int u, v;
}a[MAXN]; //存储第一次最大匹配过后的饱和点
int G[MAXN][MAXN];
int n, m, k;
int cnt;
int first[MAXN], link[MAXN];
bool vis[MAXN];
void init()
{
memset(G, 0, sizeof(G));
memset(a, 0, sizeof(a));
}
bool ED(int u)
{
for(int v = 1; v <= n; v++) if(G[u][v])
{
if(!vis[v])
{
vis[v] = 1;
if(link[v] == -1 || ED(link[v]))
{
link[v] = u;
return true;
}
}
}
return false;
}
void solve()
{
int tot = 0;
int ans = 0, IMpoint = 0;
memset(link, -1, sizeof(link));
for(int i = 1; i <= n; i++)
{
memset(vis, 0, sizeof(vis));
if(ED(i)) ans++;
}
for(int i = 1; i <= n; i++) if(link[i] != -1) //由匈牙利算法可知
{
a[tot].u = link[i], a[tot++].v = i;
}
for(int i = 0; i < tot; i++)
{
int cnt = 0;
memset(link, -1, sizeof(link));
G[a[i].u][a[i].v] = 0; //去边
for(int j = 1; j <= n; j++)
{
memset(vis, 0, sizeof(vis));
if(ED(j)) cnt++; // 写成ED(i),导致我想了一天多啊。
}
if(cnt < ans) IMpoint++;
G[a[i].u][a[i].v] = 1; //恢复
}
printf("%d important blanks for %d chessmen.\n", IMpoint, ans);
}
int main()
{
int times = 0;
while(~scanf("%d%d%d", &n, &m, &k))
{
init();
while(k--)
{
int u, v;
scanf("%d%d", &u, &v);
G[u][v] = 1;
}
printf("Board %d have ", ++times);
solve();
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAXN 110
#define MAXM 40010
struct Edge
{
int u, v;
}a[MAXN]; //存储第一次最大匹配过后的饱和点
int G[MAXN][MAXN];
int n, m, k;
int cnt;
int first[MAXN], link[MAXN];
bool vis[MAXN];
void init()
{
memset(G, 0, sizeof(G));
memset(a, 0, sizeof(a));
}
bool ED(int u)
{
for(int v = 1; v <= n; v++) if(G[u][v])
{
if(!vis[v])
{
vis[v] = 1;
if(link[v] == -1 || ED(link[v]))
{
link[v] = u;
return true;
}
}
}
return false;
}
void solve()
{
int tot = 0;
int ans = 0, IMpoint = 0;
memset(link, -1, sizeof(link));
for(int i = 1; i <= n; i++)
{
memset(vis, 0, sizeof(vis));
if(ED(i)) ans++;
}
for(int i = 1; i <= n; i++) if(link[i] != -1) //由匈牙利算法可知
{
a[tot].u = link[i], a[tot++].v = i;
}
for(int i = 0; i < tot; i++)
{
int cnt = 0;
memset(link, -1, sizeof(link));
G[a[i].u][a[i].v] = 0; //去边
for(int j = 1; j <= n; j++)
{
memset(vis, 0, sizeof(vis));
if(ED(j)) cnt++; // 写成ED(i),导致我想了一天多啊。
}
if(cnt < ans) IMpoint++;
G[a[i].u][a[i].v] = 1; //恢复
}
printf("%d important blanks for %d chessmen.\n", IMpoint, ans);
}
int main()
{
int times = 0;
while(~scanf("%d%d%d", &n, &m, &k))
{
init();
while(k--)
{
int u, v;
scanf("%d%d", &u, &v);
G[u][v] = 1;
}
printf("Board %d have ", ++times);
solve();
}
return 0;
}