AtCoder Grand Contest 074 F - Lotus Leaves

9 篇文章 0 订阅
6 篇文章 0 订阅

Time limit : 2sec / Memory limit : 256MB

Score : 800 points

Problem Statement

There is a pond with a rectangular shape. The pond is divided into a grid with H rows and W columns of squares. We will denote the square at the i-th row from the top and j-th column from the left by (i, j).

Some of the squares in the pond contains a lotus leaf floating on the water. On one of those leaves, S, there is a frog trying to get to another leaf T. The state of square (i, j) is given to you by a character aij, as follows:

. : A square without a leaf.
o : A square with a leaf floating on the water.
S : A square with the leaf S.
T : A square with the leaf T.
The frog will repeatedly perform the following action to get to the leaf T: “jump to a leaf that is in the same row or the same column as the leaf where the frog is currently located.”

Snuke is trying to remove some of the leaves, other than S and T, so that the frog cannot get to the leaf T. Determine whether this objective is achievable. If it is achievable, find the minimum necessary number of leaves to remove.

Constraints

2≤H,W≤100
aij is ., o, S or T.
There is exactly one S among aij.
There is exactly one T among aij.

Input

Input is given from Standard Input in the following format:

H W
a11 … a1W
:
aH1 … aHW

Output

If the objective is achievable, print the minimum necessary number of leaves to remove. Otherwise, print -1 instead.

Sample Input 1

3 3
S.o
.o.
o.T

Sample Output 1

2
Remove the upper-right and lower-left leaves.

Sample Input 2

3 4
S…
.oo.
…T

Sample Output 2

0

Sample Input 3

4 3
.S.
.o.
.o.
.T.

Sample Output 3

-1

Sample Input 4

10 10
.o…o..o.
….o…..
….oo.oo.
..oooo..o.
….oo….
..o..o….
o..o….So
o….T….
….o…..
……..oo

Sample Output 4

5

Gist

  • 给出一个 N*M 的网格图,上面的点有的是有跳板的,又给出起点和终点。

  • 有跳板的点可以跳到同一行或是同一列的另一个有跳板的点。

  • 问至少移走几个跳板,能使起点走不到终点。

  • 数据范围: 2N,M100

Solution

  • 经典的最小割模型。

  • 把一个点拆为两个点——一个入点和一个出点(连进来的边都连到入点,连出去的边都从出点连)。

  • 然后每一行每一列都设置一个“源点”,并将行列中的“叶子点”与其连边(容量为正无穷)。

  • 最后入点向出点连一条容量为 1 的边,代表移开这个点的代价为 1 。

  • 最后跑一遍最小割即可。

Code

#include<cstdio>
#include<cctype>
using namespace std;
const int N=105,S=N*N<<1,inf=1e9;
int tot,s,t,sum,ans;
int first[S],next[S*10],en[S*10],w[S*10];
int in[N][N],out[N][N],r[N],c[N];
int dis[S],gap[S],cur[S];
char st[N][N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void ins(int x,int y,int z)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
    w[tot]=z;
}
inline void insert(int x,int y,int z)
{
    ins(x,y,z),ins(y,x,0);
}
int sap(int x,int y)
{
    if(x==t) return y;
    int use=0;
    for(int i=cur[x];i;i=next[i])
        if(w[i] && dis[x]==dis[en[i]]+1)
        {
            cur[x]=i;
            int p=sap(en[i],min(w[i],y-use));
            w[i]-=p,w[i^1]+=p,use+=p;
            if(use==y || dis[s]>sum) return use;
        }
    cur[x]=first[x];
    if(!--gap[dis[x]]) dis[s]=sum+1;
    gap[++dis[x]]++;
    return use;
}
int main()
{
    int n=read(),m=read();
    s=++tot,t=++tot;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",st[i]+1);
        for(int j=1;j<=m;j++)
            if(st[i][j]=='o')
            {
                in[i][j]=++tot;
                out[i][j]=++tot;
            }
    }
    for(int i=1;i<=n;i++) r[i]=++tot;
    for(int i=1;i<=m;i++) c[i]=++tot;
    gap[0]=sum=tot;
    for(int i=tot=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(st[i][j]=='o')
            {
                insert(in[i][j],out[i][j],1);
                insert(r[i],in[i][j],inf);
                insert(c[j],in[i][j],inf);
                insert(out[i][j],r[i],inf);
                insert(out[i][j],c[j],inf);
            }else
            if(st[i][j]=='S')
            {
                insert(s,r[i],inf);
                insert(s,c[j],inf);
            }else
            if(st[i][j]=='T')
            {
                insert(r[i],t,inf);
                insert(c[j],t,inf);
            }
    for(int i=1;i<=sum;i++) cur[i]=first[i];
    while(dis[s]<=sum) ans+=sap(s,inf);
    printf("%d",ans>=n*m?-1:ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值