Battle shipsHDU - 5093
题目大意:n*m的地图,*代表海洋,#代表冰山,o代表浮冰,海洋上可以放置船舰,但是每一行每一列只能有一个船舰(类似象棋的車),除非同行或者同列的船舰中间有冰山挡着,问最多能放多少个船舰?
之前做过一个放置炮的,那时数据小直接暴力加搜索就A了,然而这题暴力搜索的话,直接了当的TLE,没办法只好去学新东西了。二分图这个概念只有在之前的题目中做过匈牙利的板子题,可是具体概念和思路并不了解,这题也正好提醒了我去深入了解。但最近需要做的事情较大,一直想整理的一些算法和模板总结也暂时拖一拖。
回到这题,为什么可以用二分匹配做呢,首先如果没有障碍全是海的话,那就刚好是每一行每一列只能放一个,那我们分配的时候就是给每一行分配一列,这样一行一列刚好对应了放置的位置,而这个给每一行分配一列的过程刚好也可以用二分匹配实现。那么多了障碍之后,该如何处理呢,其实也就是一行分成多行,我们把相同行的统一编号化成一块区域,举个例子,比如2*6的地图
******
******
之前每个点对行编号就是
111111
111111
而多了障碍之后
***#**
**#***
这时的编号就是
111022
330444
列的处理也是类似,列的编号就是
123056
120456
有了编号之后,我们就可以根据共同点连边,像行编号为1的点和列编号为1,2,3的点有公共点,那么它们可以连边。上图每个行块可以连的列块就是
1:1 2 3
2:5,6
3:1,2
4:4,5,6
这时我们就是尽可能多的给每一个行快分配一个列块,这样就可以得到最多的点,也就是最多能放置的数目。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 using namespace std; 5 char s[52][52]; 6 int x,y,xid[52][52],yid[52][52],cj[520],vis[520]; 7 vector<int> vv[520]; 8 bool match(int u) 9 { 10 for(int i=0;i<vv[u].size();i++) 11 { 12 int v=vv[u][i]; 13 if(!vis[v])//这个列还没遍历过 14 { 15 vis[v]=1; 16 if(!cj[v]||match(cj[v]))//这个列块还没分配给某个行块 17 {//或者它分配给的行块可以匹配其他列块 18 cj[v]=u; 19 return 1; 20 } 21 } 22 } 23 return 0; 24 } 25 int main() 26 { 27 int t,n,m; 28 scanf("%d",&t); 29 while(t--) 30 { 31 scanf("%d%d",&n,&m); 32 for(int i=0;i<n;i++) 33 scanf("%s",s[i]); 34 memset(xid,0,sizeof(xid)); 35 memset(yid,0,sizeof(yid)); 36 x=0,y=0; 37 for(int i=0;i<n;i++) 38 { 39 int j=0,is=0; 40 while(j<m) 41 { 42 if(s[i][j]==‘#‘) 43 is=0; 44 while(j<m&&s[i][j]==‘#‘) 45 j++; 46 if(s[i][j]==‘*‘) 47 { 48 if(!is) 49 x++; 50 is=1; 51 xid[i][j]=x; 52 } 53 j++; 54 } 55 }//给每个行块编号 56 for(int i=0;i<m;i++) 57 { 58 int j=0,is=0; 59 while(j<n) 60 { 61 if(s[j][i]==‘#‘) 62 is=0; 63 while(i<n&&s[j][i]==‘#‘) 64 j++; 65 if(s[j][i]==‘*‘) 66 { 67 if(!is) 68 y++; 69 is=1; 70 yid[j][i]=y; 71 } 72 j++; 73 } 74 }//给每个列块编号 75 for(int i=0;i<=x;i++) 76 vv[i].clear(); 77 for(int i=0;i<n;i++) 78 for(int j=0;j<m;j++) 79 if(s[i][j]==‘*‘)//二分图加边 80 vv[xid[i][j]].push_back(yid[i][j]); 81 int ans=0; 82 memset(cj,0,sizeof(cj)); 83 for(int i=1;i<=x;i++)//匈牙利算法模板 84 { 85 memset(vis,0,sizeof(vis)); 86 ans+=match(i); 87 } 88 printf("%d\n",ans); 89 } 90 return 0; 91 }
门泊东吴万里船
感觉二分匹配难在怎么想到这个建边
原文地址:https://www.cnblogs.com/LMCC1108/p/10642199.html