CSU2073: Tile Cut

Description

When Frodo, Sam, Merry, and Pippin are at the Green Dragon Inn drinking ale, they like to play a little game with parchment and pen to decide who buys the next round. The game works as follows: Given an m × n rectangular tile with each square marked with one of the incantations W, I, and N, find the maximal number of triominoes that can be cut from this tile such that the triomino has W and N on the ends and I in the middle (that is, it spells WIN in some order). Of course the only possible triominoes are the one with three squares in a straight line and the two ell-shaped ones. The Hobbit that is able to find the maximum number wins and chooses who buys the next round. Your job is to find the maximal number. Side note: Sam and Pippin tend to buy the most rounds of ale when they play this game, so they are lobbying to change the game to Rock, Parchment, Sword (RPS)!

Input

Each input file will contain multiple test cases. Each test case consists of an m × n rectangular grid (where 1 ≤ m, n ≤ 30) containing only the letters W, I, and N. Test cases will be separated by a blank line. Input will be terminated by end-of-file.

Output

For each input test case, print a line containing a single integer indicating the maximum total number of tiles that can be formed.

Sample Input

WIIW
NNNN
IINN
WWWI

NINWN
INIWI
WWWIW
NNNNN
IWINN

Sample Output

5
5

Hint

Source

pacnw2012

题意:有m行n列的输入,每个输入以空行结束。对于每个输入模块,求最多能删除几个连在一起的WIN(可连接的要求为上下左右相邻),输出最大操作数。

题解:W只能对上下左右的I连一条出边,I也只能对上下左右的N连一条出边,很明显是一道最大流的问题。我们建立一个源点S,S对每个W连一条容量为1的边;每个W连一条到上下左右I的边;每个I连一条到上下最后N的边;每个N连一条到汇点T的边;只需要求S到T的最大流即可。

注意,以上题解有误!!!题目要求每个点只能访问一次,而以上题解可能会导致一个点被访问多次!如两个W连一个I,那个I连两个N。。。。。因此,我们需要保证每个点只能被访问一次,这种方法叫拆点法!把每个点拆成一条容量为1的边,如此答案即为正确。

AC代码

#include <iostream>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <stdio.h>

using namespace std;

const int maxn = 3333, INF = 1000000000;
int n, m, dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};

char gg[maxn / 10][maxn / 10];

struct Edge{
    int from, to, cap, flow;
    Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f){}
};

struct EdmondsKarp{
    int n, m;
    vector<Edge> edges;
    vector<int> G[maxn];
    int a[maxn];
    int p[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);
    }

    int Maxflow(int s, int t){
        int flow = 0;
        for(;;){
            memset(a, 0, sizeof(a));
            queue<int> Q;
            Q.push(s);
            a[s] = INF;
            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(!a[e.to] && e.cap > e.flow){
                        p[e.to] = G[x][i];
                        a[e.to] = min(a[x], e.cap - e.flow);
                        Q.push(e.to);
                    }
                }
                if(a[t])
                    break;
            }
            if(!a[t])
                break;
            for(int u = t; u != s; u = edges[p[u]].from){
                edges[p[u]].flow += a[t];
                edges[p[u]^1].flow -= a[t];
            }
            flow += a[t];
        }
        return flow;
    }
};

bool check(int x, int y){
    if(x >= 0 && x < n && y >= 0 && y < m)
        return true;
    return false;
}

void solve(){
	EdmondsKarp ee;
    m = strlen(gg[0]);
    ee.n = n;
    ee.init(n);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++){
            if(gg[i][j] == 'W'){
            	ee.AddEdge(i * m + j, i * m + j + 1000, 1);
            	ee.AddEdge(n * m, i * m + j, 1);
                for(int k = 0; k < 4; k++){
                    int x = i + dir[k][0];
                    int y = j + dir[k][1];
                    if(check(x, y) && gg[x][y] == 'I')
                    	ee.AddEdge(i * m + j + 1000, x * m + y, 1);
                }
            }
            if(gg[i][j] == 'I'){
            	ee.AddEdge(i * m + j, i * m + j + 1000, 1);
                for(int k = 0; k < 4; k++){
                    int x = i + dir[k][0];
                    int y = j + dir[k][1];
                    if(check(x, y) && gg[x][y] == 'N'){
                        ee.AddEdge(i * m + j + 1000, x * m + y, 1);
                        ee.AddEdge(x * m + y + 1000, n * m + 1, 1);
                    }
                }
            }
            if(gg[i][j] == 'N')
            	ee.AddEdge(i * m + j, i * m + j + 1000, 1);
        }
    int ans = ee.Maxflow(n * m, n * m + 1);
    printf("%d\n", ans);
}

int main(){
//    freopen("in.txt", "r", stdin);
	int n1 = 0;
    while(gets(gg[n1]) != NULL){
        if(strlen(gg[n1]) == 0){
        	n = n1;
        	solve();
        	n1 = 0;
		}
		else
			n1++;
    }
    n = n1;
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值