河北省选 游戏 【例题精讲】

Description

2016年,佳缘姐姐喜欢上了一款游戏,叫做泡泡堂。简单的说,这个游戏就是在一张地图上放上若干个炸弹,看是否能炸到对手,或者躲开对手的炸弹。在玩游戏的过程中,小H想到了这样一个问题:当给定一张地图,在这张地图上最多能放上多少个炸弹能使得任意两个炸弹之间不会互相炸到。炸弹能炸到的范围是该炸弹所在的一行和一列,炸弹的威力可以穿透软石头,但是不能穿透硬石头。给定一张nm的网格地图:其中?代表空地,炸弹的威力可以穿透,可以在空地上放置一枚炸弹。x代表软石头,炸弹的威力可以穿透,不能在此放置炸弹。#代表硬石头,炸弹的威力是不能穿透的,不能在此放置炸弹。例如:给出14的网格地图xx,这个地图上最多只能放置一个炸弹。给出另一个14的网格地图x#,这个地图最多能放置两个炸弹。现在小H任意给出一张n*m的网格地图,问你最多能放置多少炸弹

Input

第一行输入两个正整数n,m,n表示地图的行数,m表示地图的列数。1≤n,m≤50。接下来输入nm列个字符,代表网格地图。的个数不超过nm

Output

输出一个整数a,表示最多能放置炸弹的个数

Sample Input 1

4 4

#***

*#**

**#*

xxx#

Sample Output 1

5

 

做过POJ222 铺木板 的同学应该知道,这种题是二分图相关的问题;

我们知道,在二分图以至整个图论部分都有一种很神奇的方法,叫抽象;有不少这样的题,都是把边(或行列)抽象为“点”,把边与边的交叉点抽象为边,然后不知怎么的,就成了一个匈牙利模板......

那么今天,我就讲讲这个“不知怎么的”的过程;

 

 

 

首先,我们按照图论的思路,把横行可炸块定义为左部点,把竖列可炸块定义为右部点,这样,如果两个块有空地(可放炸弹)点的交叉,那就连一条有向边,那么问题来了,我要怎么存这个点呢,好,这里科普一个方法,用这个行或列可炸快的第一个点的编号作为这个块的编号,而这个点的编号,当然就是他在整个图是第几个点咯,我们可以横折数,当然也可以竖着,这个表示方式是很自由的!
那为毛这道题是跑最大匹配呢,试想,炸弹不能互相炸,其实表现在抽象之后的图,也就是使图中边与边之间没有公共点,求这样的边最多有几条,那不就是最大匹配的定义吗?

 
匹配是图中一些边的集合,且集合中任意两条边都没有公共点,所有的匹配中,边数最多的就是最大匹配。
那这道题不就出来了么?
看代码......
 
 
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<string>
 6 #include<cstdlib>
 7 #include<queue>
 8 #include<algorithm>
 9 using namespace std;
10 const int MAXN=2505;
11 struct node{
12     int x,y;
13     int next;
14 }edge[MAXN];
15 char map[51][51];
16 int n,m,k,match[MAXN];
17 int tot=0,ans=0,cnt=0;
18 int head[MAXN];
19 bool vis[MAXN];
20 void add(int x,int y)
21 {
22     edge[++tot].y=y;
23     edge[tot].x=x;
24     edge[tot].next=head[x];
25     head[x]=tot;
26 }
27 bool dfs(int x)//二分图最大匹配; 
28 {
29     for(int i=head[x];i;i=edge[i].next){
30         int y=edge[i].y;
31         if(!vis[y]){
32             vis[y]=true;
33             if(!match[y]||dfs(match[y])){
34                 match[y]=x;return 1;
35             }
36         }
37     }
38     return 0;
39 } 
40 int main()
41 {
42     scanf("%d%d",&n,&m);
43     for(int i=1;i<=n;i++){
44         scanf("%s",map[i]+1);
45     }
46     for(int i=1;i<=n;i++){
47         for(int j=1;j<=m;j++){
48             int x=i,y=j;
49             if(map[x][y]=='*'){
50                 while(x>1&&map[x-1][j]!='#')x--;
51                 while(y>1&&map[i][y-1]!='#')y--;
52                 add(m*(i-1)+y,m*(j-1)+x);//将每一个可炸行和可炸列记作第一个可炸点的编号; 
53             }
54         }
55     }
56     for(int i=1;i<=n*m;i++){
57         memset(vis,false,sizeof(vis));
58         if(dfs(i)) cnt++;
59     }
60     printf("%d",cnt);
61     puts("");
62     return 0;
63 }
View Code

 

 

posted @ 2018-04-05 10:20  杜宇一声  阅读(235)  评论(0编辑  收藏  举报