本题有权限……
1458: 士兵占领
时限: 10s
空间上限: 64 MB
题目描述
有一个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
首先容易想到如何判断无解,把棋盘上能放的放,看一下是否合法,然后可以沿这个思路走下去,看最多能拿多少。
网络流建模,源点向所有行连边,容量上限为最多能拿走多少,所有列连边,容量上限同样为最多能拿走多少。所有能放的格子所在行列连一边,容量为1。Dinic模板AC。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int INF=(((1<<30)-1)<<1)+1;
int n,m,k,tot,ans,sum,s,t,son[20005],nxt[20005],cap[20005],flow[20005],lnk[205],que[205],dst[205],lst[205],a[105],b[105];
bool mp[105][105],vs[205];
inline void readi(int &x){
x=0; char ch=getchar();
while ('0'>ch||ch>'9') ch=getchar();
while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
}
void _add(int x,int y,int z){
tot++; son[tot]=y; nxt[tot]=lnk[x]; cap[tot]=z; flow[tot]=0; lnk[x]=tot;
tot++; son[tot]=x; nxt[tot]=lnk[y]; cap[tot]=0; flow[tot]=0; lnk[y]=tot;
}
void _write(){printf("JIONG!"); exit(0);}
bool _bfs(){
memset(vs,0,sizeof(vs));
memset(dst,0,sizeof(dst));
int hed=0,til=1; que[1]=s; vs[s]=true; dst[s]=1;
while (hed!=til){
int x=que[++hed];
for (int j=lnk[x];j;j=nxt[j])
if (!vs[son[j]]&&cap[j]>flow[j]){
que[++til]=son[j];
dst[son[j]]=dst[x]+1;
vs[son[j]]=true;
}
}
return vs[t];
}
int _dfs(int x,int now){
if (!now||x==t) return now; int sum=0;
for (int j=lst[x];j;lst[x]=j=nxt[j])
if (dst[son[j]]==dst[x]+1){
int tem=_dfs(son[j],min(now,cap[j]-flow[j]));
if (tem){
sum+=tem; now-=tem; flow[j]+=tem; flow[j^1]-=tem;
if (!now) break;
}
}
return sum;
}
int _Dinic(){
ans=0;
while (_bfs()){
for (int i=1;i<=t;i++) lst[i]=lnk[i];
ans+=_dfs(s,INF);
}
return ans;
}
int main(){
freopen("soldier.in","r",stdin);
freopen("soldier.out","w",stdout);
readi(n); readi(m); readi(k); tot=1; s=n+m+1; t=s+1;
memset(mp,1,sizeof(mp));
memset(lnk,0,sizeof(lnk));
for (int i=1;i<=n;i++) readi(a[i]);
for (int i=1;i<=m;i++) readi(b[i]);
for (int i=1,x,y;i<=k;i++){
readi(x); readi(y); mp[x][y]=0;
}
for (int i=1;i<=n;i++){
int tem=0;
for (int j=1;j<=m;j++) tem+=mp[i][j];
if (tem<a[i]) _write();
_add(s,i,tem-a[i]);
}
for (int j=1;j<=m;j++){
int tem=0;
for (int i=1;i<=n;i++) tem+=mp[i][j];
if (tem<b[j]) _write();
_add(j+n,t,tem-b[j]);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (mp[i][j]){_add(i,j+n,1); sum++;}
printf("%d",sum-_Dinic());
return 0;
}

解决一个M*N的棋盘上放置最少数量士兵的问题,确保每个指定行和列都有足够的士兵覆盖,并避开障碍物。采用网络流算法进行求解。
328

被折叠的 条评论
为什么被折叠?



