BZOJ 1458: 士兵占领 最大流

1458: 士兵占领

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1099  Solved: 618
[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


邻接表开小 怒RE n 发

本来想写上下界  然而。。。不会写。。。

看看以后能不能填上坑

所以

贴上hzwer的题解: 
此题的思路是先放满棋盘,然后考虑最多可以删多少个。。。 
某一行和某一列的可以放的格子数小于需求就直接jiong掉 
然后从源向每一行连边,流量为可以放的格子数 - 需求的格子数(也就是可以删多少格子) 
从每一列向汇,同上. 
从每一个非障碍的格子的行向列连边流量为1 
跑一遍最大流即可ans=可放格子数-maxflow

咦怎么感觉他的更科学。。。


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<queue>
#include<set>
#include<map>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return f*x;
}
const int N=110;const int inf=0X7f7f7f7f;
int n,m,k,ecnt=1,last[N<<1],L[N],C[N],antiL[N],antiC[N],d[N<<1],q[N<<1],a[N][N],S,T=(N<<1)-2,ans;
struct EDGE{int to,nt,val;}e[N<<7];
inline void readd(int u,int v,int val)
{e[++ecnt]=(EDGE){v,last[u],val};last[u]=ecnt;}
inline void add(int u,int v,int val)
{readd(u,v,val);readd(v,u,0);}
bool bfs()  
{  
    memset(d,0,sizeof(d));  
    int t=0,w=1;q[t]=S;d[S]=1;  
    while(t<w)  
    {  
        int temp=q[t++];  
        for(int i=last[temp];i;i=e[i].nt)  
        if(e[i].val&&!d[e[i].to])  
        {d[e[i].to]=d[temp]+1;q[w++]=e[i].to;}  
    }  
    if(d[T])return true;else return false;  
}  
inline int dfs(int u,int lim)  
{  
    if(u==T||lim==0)return lim;  
    int ret=0;  
    for(int i=last[u];i;i=e[i].nt)if(d[e[i].to]==d[u]+1)  
    {  
        int temp=dfs(e[i].to,min(e[i].val,lim));  
        e[i].val-=temp;e[i^1].val+=temp;  
        lim-=temp;ret+=temp;  
        if(lim==0)break;  
    }  
    if(!ret)d[u]=-1;  
    return ret;  
}  
void dinic()
{while(bfs()){ans+=dfs(S,inf);}}
int main()
{
	n=read();m=read();k=read();int sum=n*m-k;
	for(int i=1;i<=n;i++)L[i]=read();
	for(int i=1;i<=m;i++)C[i]=read();
	for(int i=1;i<=k;i++)a[read()][read()]=1;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{if(a[i][j])antiL[i]++,antiC[j]++;else add(i,n+j,1);}
	for(int i=1;i<=n;i++)
	if(antiL[i]+L[i]>m){puts("JIONG!");return 0;}
	else add(S,i,m-antiL[i]-L[i]);
	for(int i=1;i<=m;i++)
	if(antiC[i]+C[i]>n){puts("JIONG!");return 0;}
	else add(i+n,T,n-antiC[i]-C[i]);
//	for(int i=1;i<=ecnt;i++)cout<<e[i].to<<endl; 
	dinic();
	printf("%d\n",sum-ans);
	return 0;
} 
/*
4 4 4
1 1 1 1
0 1 0 3
1 4
2 2
3 3
4 3

Sample Output

4
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值