HDU 5093 Battle ships(二分图匹配)

该题是一道经典的二分图匹配题目 。  同一列(行)上不能放两个船除非有冰山隔着。对于这种二维平面图,我们很容易想到将行和列分成两个集合,进行二分图匹配,当一个行坐标匹配到一个列坐标时,该格子可以放置船。那么为了使任意两个船都不在同一行或者同一列,除非有冰山,我们可以将每一行中一块连续的只能放置一个船的区域都设成一个编号,同样的按照列也这样处理,这样就相当于将行和列缩点了,接下来用最大流模板套一套就可以了 。

处理二分图还有一种更好的算法,叫匈牙利算法,紫书上没有,先用最大流算法解决吧 。

紫书十一章还有一道相似的题目,不过更加有趣 传送门

细节参见代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2*550*550;
typedef long long ll;
const int INF = 1000000000;
int T,n,m,id1[55][55],id2[55][55];
struct Edge {
  int from, to, cap, flow;
};
bool operator < (const Edge& a, const Edge& b) {
  return a.from < b.from || (a.from == b.from && a.to < b.to);
}
struct Dinic {
  int n, m, s, t;
  vector<Edge> old;
  vector<Edge> edges;    // 边数的两倍
  vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
  bool vis[maxn];        // BFS使用
  int d[maxn];           // 从起点到i的距离
  int cur[maxn];         // 当前弧指针
void init(int n) {
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
}
void AddEdge(int from, int to, int cap) {
    edges.push_back((Edge){from, to, cap, 0});
    edges.push_back((Edge){to, from, 0, 0});
    m = edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
}
bool BFS() {
    memset(vis, 0, sizeof(vis));
    queue<int> Q;
    Q.push(s);
    vis[s] = 1;
    d[s] = 0;
    while(!Q.empty()) {
      int x = Q.front(); Q.pop();
      for(int i = 0; i < G[x].size(); i++) {
        Edge& e = edges[G[x][i]];
        if(!vis[e.to] && e.cap > e.flow) {
          vis[e.to] = 1;
          d[e.to] = d[x] + 1;
          Q.push(e.to);
        }
      }
    }
    return vis[t];
}
int DFS(int x, int a) {
    if(x == t || a == 0) return a;
    int flow = 0, f;
    for(int& i = cur[x]; i < G[x].size(); i++) {
      Edge& e = edges[G[x][i]];
      if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
        e.flow += f;
        edges[G[x][i]^1].flow -= f;
        flow += f;
        a -= f;
        if(a == 0) break;
      }
    }
    return flow;
}
int Maxflow(int s, int t) {
    this->s = s; this->t = t;
    int flow = 0;
    while(BFS()) {
      memset(cur, 0, sizeof(cur));
      flow += DFS(s, INF);
    }
    return flow;
  }
}g;
map<int,int> p;
char s[55][55];
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        g.init(maxn);
        for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
        int cnt = 1;
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=m;j++) {
                if(s[i][j] == '#') id1[i][j] = cnt++;
                if(s[i][j] == '*') id1[i][j] = cnt;
            }
            cnt++;
        }
        for(int j=1;j<=m;j++) {
            for(int i=1;i<=n;i++) {
                if(s[i][j] == '#') id2[i][j] = cnt++;
                if(s[i][j] == '*') id2[i][j] = cnt;
            }
            cnt++;
        }
        p.clear();
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=m;j++) {
                if(s[i][j] == '*') {
                    g.AddEdge(id1[i][j],id2[i][j],1);
                    if(!p.count(id1[i][j])) {
                        p[id1[i][j]] = 1;
                        g.AddEdge(0,id1[i][j],1);
                    }
                    if(!p.count(id2[i][j])) {
                        p[id2[i][j]] = 1;
                        g.AddEdge(id2[i][j],cnt+1,1);
                    }
                }
            }
        }
        int ans = g.Maxflow(0,cnt+1);
        printf("%d\n",ans);
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-04 15:36:08

HDU 5093 Battle ships(二分图匹配)的相关文章

hdu 5093 Battle ships 二分图匹配

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5093 一开始就往贪心的方向想了结果wa全场 这种矩阵形式的图一般要联想到行和列构成了二分图 然后实质就是求一个最大匹配 中间的冰山实际上就是把一行或一列切成多个顶点而已 所以一开始预处理一下 然后就可以套用模板 #include <cstring> #include <cstdlib> #include <cstring> #include <cmath> #i

HDOJ 5093 Battle ships 二分图匹配

二分图匹配: 分别按行和列把图展开,hungary二分图匹配.... 样例: 4 4 *ooo o### **#* ooo* 按行展开.... *ooo o#oo oo#o ooo# **#o ooo* ooo* 再按列展开... 7 * 8 *ooooooo oooooooo oooooooo oooooooo *o*ooooo ooooooo* ooooooo* 匹配结果3 Battle ships Time Limit: 2000/1000 MS (Java/Others)    Memo

hdu 5093 Battle ships二分图

二分图最大匹配问题 遇到冰山就把行列拆成两个部分.每个部分x也好,y也好只能匹配一次 图画得比较草,将就着看 横着扫一遍,竖着扫一遍,得到编号 一个位置就对应一个(xi,yi)就是X集到Y集的一条边, 由题意,每个点只能被选择一次.所以最大匹配的边数就是答案了. 算法过程 通常都是先贪心求一个匹配,然后开始增广. 寻找增广路的过程: 一个没有和任意边匹配的点叫做未盖点,从左集X中一个未盖点u出发寻找增广路. 从u出发,选一个非匹配边到达Y集中的v,如果v没匹配,那么就找到一条增广路(只要把之前走

hdu 5093 Battle ships 二分匹配

题意:在n×m的方格中,'#'代表iceberg,'*'代表ocean,'o'代表floating ice.战舰只能放在ocean上,在同一行或者同一列不能放两 个战舰除非它们中间有iceberg,求最多能放多少战舰. 思路:二分匹配.每行中连续为'*'的作为X集合中一个点,同样,将每列中连续为'*'的点作为Y集合中的一个点.对原图中每个'*',将其对应的X集合和Y集合中的标号建边,便形成了二分图,对该图求最大匹配.详见代码: /*********************************

hdu 5093 Battle ships 最大二分匹配

Battle ships Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 589    Accepted Submission(s): 233 Problem Description Dear contestant, now you are an excellent navy commander, who is responsible

hdu 5093 Battle ships

二分图匹配 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cmath> 5 #define maxn 60 6 #define maxd 1500 7 using namespace std; 8 int v[maxd][maxd],vist[maxd],math[maxd]; 9 10 int col[maxn][maxn],row[maxn][maxn]; 1

hdu 5093 Battle ships (二部图+最大匹配)

Battle ships Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 172    Accepted Submission(s): 84 Problem Description Dear contestant, now you are an excellent navy commander, who is responsible o

hdu 5093 Battle ships(二分图最大匹配)

题意: M*N的矩阵,每个格子上是三个之一:*.o.#.                     (1 <= m, n <= 50) *:海洋,战船可以停在上面.      o:浮冰,战船不可以停在上面      #:冰山,战船不可以停在上面. 限制:两艘战船不能处于同一行或同一列,除非它们之间有冰山挡着. 问最多可以停多少艘战船. 思路: 和二分图最小点覆盖那道经典题很相似.不过不是求最小点覆盖. 对于每一行,如果连续的一段只能放一艘战船,则将这一段视为同一个点.则每一行都被分为若干个小段,

hdu 5093 Battle ships 匈牙利 很巧妙的建图思路

//这题逼我把匈牙利学了 之前一直很勤快敲网络流 而且不以为耻反以为荣 解:首先按行扫描编号,如果在同一块中(即可以相互攻击),那么将其标为相同的数组,对列也做同样的操作. 然后扫描整张图,如果行编号为a的块与列编号为b的块有公共点,那么将二部图中A集合中a点与B集合中b点相连.最后求出来最大二分匹配数就是答案. (为什么这样做)首先很明显的,二部图中每一条边就对应原图中的一个点,因此,匹配数=边数=最多可放置的战舰数,另外二分图每个点只能匹配一次,对应到原题中就是每一块只能放置一个战舰. 1