【bzoj1458】【士兵占领】【网络流】

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
题解:
源点向每一行连容量为该行所需士兵数量的边.
每一列向汇点连容量为该列所需士兵数量的边.
对于每个可以摆放的点(x,y).
x向y连容量为1的边.
sigma(L[i])+sigma(C[i])-最小割即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300
#define M 20000 
#define inf 2100000000
using namespace std;
int n,m,x,y,k,sum1,sum2,T,next[M<<1],point[N],h[N],l[N];
int dis[N],pre[N],gap[N],cur[N],cnt=1,map[N][N],num1[N],num2[N];
bool f;
struct use{int st,en,v;}e[M<<1];
void add(int x,int y,int v){
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=point[y];point[y]=cnt;
  e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;
}
int isap(){
  int i,mn,ans(0),u(1);
  gap[0]=T;
  for (i=1;i<=T;i++) cur[i]=point[i];
  while (dis[1]<T){
    f=false;
    for (i=cur[u];i;i=next[i])
      if (e[i].v&&dis[e[i].en]+1==dis[u]){cur[u]=i;f=true;break;}
    if (f){
      pre[u=e[i].en]=i;
      if (u==T){
        mn=inf;
        for (i=T;i!=1;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
        ans+=mn;
        for (i=T;i!=1;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn;
        u=1;
      }
    }
    else{
      gap[dis[u]]--;if (!gap[dis[u]]) return ans;
      for (mn=T,i=point[u];i;i=next[i]) 
        if (e[i].v) mn=min(mn,dis[e[i].en]);
      gap[dis[u]=mn+1]++;cur[u]=point[u];
      if (u!=1) u=e[pre[u]].st;
    }
  }
  return ans;
}
int main(){
  scanf("%d%d%d",&n,&m,&k);T=n+m+2;
  for (int i=1;i<=n;i++){
    scanf("%d",&h[i]);
    add(1,i+1,h[i]);
    sum1+=h[i];
  }
  for (int i=1;i<=m;i++){
    scanf("%d",&l[i]);
    add(i+n+1,T,l[i]);
    sum2+=l[i];
  } 
  for (int i=1;i<=k;i++){
    scanf("%d%d",&x,&y);
    map[x][y]=1;                  
  }             
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
    if (!map[i][j])
      add(i+1,j+n+1,1),num1[i]++,num2[j]++;
  for (int i=1;i<=n;i++)if (num1[i]<h[i]){cout<<"JIONG!";return 0;}
  for (int i=1;i<=m;i++)if (num2[i]<l[i]){cout<<"JIONG!";return 0;}
  int t=sum1+sum2-isap();
  cout<<t<<endl;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值