有一个M * N的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第i行至少放置了Li个士兵, 第j列至少放置了Cj个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。
有一个M * N的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第i行至少放置了Li个士兵, 第j列至少放置了Cj个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。
第一行两个数M, N, K分别表示棋盘的行数,列数以及障碍的个数。 第二行有M个数表示Li。 第三行有N个数表示Ci。 接下来有K行,每行两个数X, Y表示(X, Y)这个格子是障碍。
输出一个数表示最少需要使用的士兵个数。如果无论放置多少个士兵都没有办法占领整个棋盘,输出”JIONG!” (不含引号)
考虑为什么源点向放置士兵的位置连边为2?我是这么想的,因为一个位置不仅需要向他所在的列连边还需要向他所在的行连边,但是行和列是互不影响的,所以只能分别补充,因为补充的容量为1,所以每个点只能算一次。
虽然AC了,但是本人还是觉得不是很科学,看了看网上的标解:
觉得还是标解科学。。。。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #define N 200000 #define M 1000000 using namespace std; int n,m,k; int tot=-1,l[120],h[120],a[120][120],deep[N]; int point[N],dis[N],can[N],laste[N],mincost,maxflow; int next[M*2],v[M*2],remain[M*2],c[M*2],cur[N],num[N]; int fl[N],fh[N]; const int inf=1e9; void add(int x,int y,int z) { tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; } int addflow(int s,int t) { int now=t; int ans=inf; while (now!=s) { ans=min(ans,remain[laste[now]]); now=v[laste[now]^1]; } now=t; while (now!=s) { remain[laste[now]]-=ans; remain[laste[now]^1]+=ans; now=v[laste[now]^1]; } return ans; } void bfs(int s,int t) { for (int i=s;i<=t;i++) deep[i]=t+1; deep[t]=0; queue<int> p;p.push(t); while (!p.empty()) { int now=p.front(); p.pop(); for (int i=point[now];i!=-1;i=next[i]) if (deep[v[i]]==t+1&&remain[i^1]) deep[v[i]]=deep[now]+1,p.push(v[i]); } } void isap(int s,int t) { for (int i=s;i<=t;i++) cur[i]=point[i]; for (int i=s;i<=t;i++) num[deep[i]]++; bfs(s,t); int now=s; while (deep[s]<=t) { if (now==t) { maxflow+=addflow(s,t); now=s; } bool f=false; for (int i=cur[now];i!=-1;i=next[i]) { if (deep[v[i]]+1==deep[now]&&remain[i]) { laste[v[i]]=i; f=true; cur[now]=i; now=v[i]; break; } } if (!f) { int minn=t; for (int i=point[now];i!=-1;i=next[i]) if (remain[i]) minn=min(minn,deep[v[i]]); if (!--num[deep[now]]) break; deep[now]=minn+1; num[deep[now]]++; cur[now]=point[now]; if (now!=s) now=v[laste[now]^1]; } } } int main() { memset(point,-1,sizeof(point)); memset(next,-1,sizeof(next)); scanf("%d%d%d",&n,&m,&k); int sum=0; for (int i=1;i<=n;i++) scanf("%d",&l[i]),sum+=l[i]; for (int i=1;i<=m;i++) scanf("%d",&h[i]),sum+=h[i]; for (int i=1;i<=k;i++) { int x,y; scanf("%d%d",&x,&y); a[x][y]=1; } int num=n*m; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (!a[i][j]) { int x=(i-1)*m+j; add(0,x,2); add(x,num+i,1); add(x,num+n+j,1); fl[i]++; fh[j]++; } for (int i=1;i<=n;i++) if (fl[i]<l[i]) { printf("JIONG!\n"); return 0; } for (int i=1;i<=m;i++) if (fh[i]<h[i]) { printf("JIONG!\n"); return 0; } int cnt=n*m+n+m+1; for (int i=1;i<=n;i++) add(num+i,cnt,l[i]); for (int i=1;i<=m;i++) add(num+n+i,cnt,h[i]); isap(0,cnt); printf("%d\n",maxflow/2); return 0; }