【bzoj 1458】士兵占领(最大流)

45 篇文章 0 订阅
16 篇文章 0 订阅

1458: 士兵占领

Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 795 Solved: 462
[Submit][Status][Discuss]
Description

有一个M * N的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第i行至少放置了Li个士兵, 第j列至少放置了Cj个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。

Input

第一行两个数M, N, K分别表示棋盘的行数,列数以及障碍的个数。 第二行有M个数表示Li。 第三行有N个数表示Ci。 接下来有K行,每行两个数X, Y表示(X, Y)这个格子是障碍。

Output

输出一个数表示最少需要使用的士兵个数。如果无论放置多少个士兵都没有办法占领整个棋盘,输出”JIONG!” (不含引号)

Sample Input

4 4 4

1 1 1 1

0 1 0 3

1 4

2 2

3 3

4 3

Sample Output

4

数据范围

M, N <= 100, 0 <= K <= M * N

HINT

Source

题解】【网络流最大流】
【刚看到这道题 woc 这不搜索?!然而小伙伴华丽丽网络流A掉……】
网络流,考虑将所有可以放棋子的地方都放满,然后求最多可以去掉几个,最后用总数减去去掉的二分之一(因为拆点了,所以每个点算了两遍)
将所有对行和列的要求读进来,然后算出每行每列可以最多空几个,然后再删去每行每列不能放的,每个不能放棋子的点要记录,判断去掉不能放的点后每行每列剩下的可以空着的点是否大于等于零,如果小于零了,则说明当前情况下不可能满足题目要求,直接输出JIONG!
建图:拆点(准确的说是拆行和列),将源点与每行每列连边,每行每列与汇点连边,流量为前面求出的剩下的点的个数,每行每列分别和与其相关的列和行连边,流量为1。

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[500010],next[500010],p[510],remain[500010],tot,sz;
int dis[510],cur[510];
int h[110],l[110];
int n,m,k,S,T,ans,s;
bool b[110][110];
inline void add(int x,int y,int flow)
{
    tot++; a[tot]=y; next[tot]=p[x]; p[x]=tot; remain[tot]=flow;
    tot++; a[tot]=x; next[tot]=p[y]; p[y]=tot; remain[tot]=0;
}
inline bool bfs(int s,int t)
{
    queue<int>que;
    memset(dis,-1,sizeof(dis));
    for(int i=s;i<=t;++i) cur[i]=p[i];
    dis[s]=0; que.push(s);
    while(!que.empty())
     {
        int u,v;
        u=que.front(); que.pop();
        v=p[u];
        while(v!=-1)
         {
            if (remain[v]&&dis[a[v]]<0)
             {
                dis[a[v]]=dis[u]+1;
                que.push(a[v]);
               }
            v=next[v];
          }
     }
    if (dis[t]<0) return false;
     else return true;
}
inline int dfs(int now,int t,int flow)
{
    if(!flow||now==t) return flow;
    int u=cur[now],s1=0,s;
    while(u!=-1)
     {
        cur[now]=u;
        if (dis[a[u]]==dis[now]+1&&(s=dfs(a[u],t,min(flow,remain[u]))))
         {
            s1+=s; flow-=s;
            remain[u]-=s; remain[u^1]+=s;
            if (!flow) break;
          }
        u=next[u];
     }
    return s1;
}
int main()
{
    int i,j,sum=0;
    memset(b,true,sizeof(b));
    memset(next,-1,sizeof(next));
    memset(p,-1,sizeof(p));
    scanf("%d%d%d",&n,&m,&k);
    for (i=1;i<=n;++i) scanf("%d",&h[i]),h[i]=m-h[i];
    for (i=1;i<=m;++i) scanf("%d",&l[i]),l[i]=n-l[i];
    sum=n*m;
    for (i=1;i<=k;++i)
     {
        int x,y;
        scanf("%d%d",&x,&y);
        b[x][y]=false;
        --h[x]; --l[y];sum--;
        if (h[x]<0||l[y]<0)
           {printf("JIONG!\n"); return 0;}
     }
    T=(n+m)*2+1; tot=-1; int t=n+m; S=0;
    for (i=1;i<=n;++i) add(S,i,h[i]),add(i+t,T,h[i]);
    for (i=1;i<=m;++i) add(S,i+n,l[i]),add(i+n+t,T,l[i]);
    for (i=1;i<=n;++i)
     for (j=1;j<=m;++j)
      if (b[i][j])
        add(i,j+n+t,1),add(j+n,i+t,1);
    while(bfs(S,T))
     while(s=dfs(S,T,0x7fffffff))
      ans+=s;
    sum-=ans/2;
    printf("%d\n",sum);
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值