题目链接:http://codeforces.com/gym/100203/problem/I
题意:给一个由'W','I','N'三种字母组成的n*m的矩阵,现在要从中截取 'WIN' ,要求'I'一定要在'W'和'N'的中间,问最多能截取多少个'WIN'。
当然截取的意思就是一个字母用了一次就没有了,一开始以为是搜索,后来wa了几次以后反应过来搜索是不可能搜索的,其实这是一道最大流的题,只要想到了怎么建图,那就是一道模板题。
建图方式:建一个超级源点,所有的'W'和源点连一条边,然后按照题目要求要组成'WIN'的话,那么建边应该是'W' - 'I' - 'N'这样子连边才能满足题目的要求,'N'与超级汇点连边,但是这里有一个问题,就是题目是要求截取这个'WIN',那么一个字母肯定只能被用一次,如果我们直接 'S' - 'W' - 'I' - 'N' - 'T'这样子建边,那么一个'I'很有可能被多个 'W' 和 'N' 同时使用,所以为了避免这种情况,我们对每个'I',建一条 'I' 到 'I' 的边,保证每个'I' 只能使用一次。到这里为止这道题就很清楚了,只要知道了如何建边,跑一边最大流就是答案了,在这题里面还有一个问题就是,给出的点都在一个二维的矩阵中,那么对于每个点要用它来建图的话,可以转化成一维的。在这题里面对于点(i,j)位置,可以用i+j*30表示,是不会重合的。最后,模板很重要。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#define INF 100000000
using namespace std;
char s[50][50];
int dep[10009],n,m,s1,t1;
struct H{
int to,cap,rev;
};
vector <H> v[10009];
void add(int f,int t,int c)
{
v[f].push_back((H){t,c,v[t].size()});
v[t].push_back((H){f,0,v[f].size()-1});
}
int bfs(int s,int t)
{
memset(dep,-1,sizeof(dep));
queue <int> que;
while(!que.empty()) que.pop();
que.push(s);
dep[s]=0;//!!!
while(!que.empty())
{
int k=que.front();
que.pop();
for(int i=0;i<v[k].size();i++)
{
H &tmp=v[k][i];
if(dep[tmp.to]==-1&&tmp.cap>0)
{
dep[tmp.to]=dep[k]+1;
que.push(tmp.to);
}
}
}
return dep[t]!=-1;
}
int dfs(int s,int t,int f)
{
int d;
if(s==t) return f;
for(int i=0;i<v[s].size();i++)
{
H &tmp=v[s][i];
if(tmp.cap>0&&dep[tmp.to]==dep[s]+1&&(d=dfs(tmp.to,t,min(f,tmp.cap))))
{
v[s][i].cap-=d;
v[tmp.to][tmp.rev].cap+=d;
return d;
}
}
return 0;
}
int dinic(int s,int t)
{
int flow=0;
while(bfs(s,t))
{
while(1)
{
int d=dfs(s,t,INF);
if(d==0) break;
flow+=d;
}
}
return flow;
}
int dx[4]= {1,-1,0,0};
int dy[4]= {0,0,1,-1};
int nn,mm;
int judge(int x,int y)
{
if(x<=0||x>nn)
return 0;
if(y<=0||y>mm)
return 0;
return 1;
}
int main()
{
scanf("%d%d",&nn,&mm);
//init(15000);//初始化所有的点
for(int i=1; i<=nn; i++)
scanf("%s",s[i]+1);
for(int i=1; i<=nn; i++)
{
for(int j=1; j<=mm; j++)
{
if(s[i][j]=='I')//对于每一个'I'字母,'I'到'I'建一条权值为1的边
{
add(i+j*30,i+j*30+1000,1);//使用i+j*30转化二维坐标
}
}
}
for(int i=1; i<=nn; i++)
{
for(int j=1; j<=mm; j++)
{
if(s[i][j]=='W')//对于每一个'W'坐标,源点到w建一条权值为1的边
{
add(4000,i+j*30,1);//同样的要转化成一维的
for(int k=0; k<4; k++)
{
if(judge(i+dx[k],j+dy[k]))
{
//如果上下左右有相连的I的话,在'W'和'I'之间加一条权值为1的边
if(s[i+dx[k]][j+dy[k]]=='I')
{
int ii=i+dx[k],jj=j+dy[k];
add(i+j*30,ii+jj*30,1);
}
}
}
}
if(s[i][j]=='I')//
{
for(int k=0; k<4; k++)
{
if(judge(i+dx[k],j+dy[k]))
{
if(s[i+dx[k]][j+dy[k]]=='N')
{
int ii=i+dx[k],jj=j+dy[k];
add(i+j*30+1000,ii+jj*30,1);
}
}
}
}
if(s[i][j]=='N')
{
add(i+j*30,4001,1);
}
}
}
printf("%d\n",dinic(4000,4001));
return 0;
}