Gym 100203 I. WIN(网络流最大流)

题目链接: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
#include
#include
#include
#include
#include
#include
#define INF 100000000
using namespace std;
char s[50][50];
int dep[10009],n,m,s1,t1;
struct H{
    int to,cap,rev;
};
vector 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 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         {
            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     {
        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;
}

你可能感兴趣的:(网络流)