[BZOJ 1458] 士兵占领
1458: 士兵占领
Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 1210 Solved: 667
[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
对于这道题,如果每个士兵仅对某一行或者某一列而不是两者同时作出贡献,则答案$ans$为:
\[ ans=\sum_{i=1}^m l_i +\sum_{i=1}^n c_i \]
但是我们知道一些士兵可以同时对某行和某列的和作出贡献,所以我们可以求这样的士兵的最大值,最后用上面求出的假的\(ans\)减去就可以了
而求最大值可以考虑网络流,将可以放置士兵的位置的行结点与列结点之间连一条容量为$1$的边,行/列结点分别向源点$s$ / 汇点$t$ 连一条容量为对应需求 $l_i$ 或 $c_i$的边再跑一遍最大流就可以得出结果.
至于无解情况网络流则无法处理,要在计算最大流之前进行枚举特判.
参考代码
1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <iostream> 6 #include <algorithm> 7 8 const int MAXV=210; 9 const int MAXE=40100; 10 const int INF=0x7FFFFFFF; 11 12 struct Edge{ 13 int from; 14 int to; 15 int flow; 16 Edge* rev; 17 Edge* next; 18 }; 19 20 Edge E[MAXE]; 21 Edge* head[MAXV]; 22 Edge* top=E; 23 24 int m; 25 int n; 26 int k; 27 int sum; 28 int c[MAXV]; 29 int l[MAXV]; 30 int depth[MAXV]; 31 bool flag[MAXV][MAXV]; 32 33 int Dinic(int,int); 34 int DFS(int,int,int); 35 int Convert(int,int); 36 bool BFS(int,int); 37 bool Check(); 38 void Build(); 39 void Initialize(); 40 void Insert(int,int,int); 41 42 int main(){ 43 Initialize(); 44 if(!Check()){ 45 puts("JIONG!"); 46 } 47 else{ 48 Build(); 49 printf("%d\n",sum-Dinic(0,m+n+1)); 50 } 51 return 0; 52 } 53 54 int Dinic(int s,int t){ 55 int ans=0; 56 while(BFS(s,t)){ 57 ans+=DFS(s,INF,t); 58 } 59 return ans; 60 } 61 62 int DFS(int s,int flow,int t){ 63 if(s==t||flow==0) 64 return flow; 65 int tmp=flow; 66 int k; 67 for(Edge* i=head[s];i!=NULL;i=i->next){ 68 if(i->flow!=0&&tmp!=0&&depth[i->to]==depth[s]+1){ 69 k=DFS(i->to,std::min(tmp,i->flow),t); 70 if(k==0){ 71 depth[i->to]=0; 72 continue; 73 } 74 tmp-=k; 75 i->flow-=k; 76 i->rev->flow+=k; 77 if(tmp==0) 78 break; 79 } 80 } 81 return flow-tmp; 82 } 83 84 bool BFS(int s,int t){ 85 memset(depth,0,sizeof(depth)); 86 std::queue<int> q; 87 q.push(s); 88 depth[s]=1; 89 while(!q.empty()){ 90 s=q.front(); 91 q.pop(); 92 for(Edge* i=head[s];i!=NULL;i=i->next){ 93 if(depth[i->to]==0&&i->flow!=0){ 94 depth[i->to]=depth[s]+1; 95 q.push(i->to); 96 if(i->to==t) 97 return true; 98 } 99 } 100 } 101 return false; 102 } 103 104 void Build(){ 105 for(int i=1;i<=m;i++){ 106 for(int j=1;j<=n;j++){ 107 if(!flag[i][j]) 108 Insert(i,j+m,1); 109 } 110 } 111 } 112 113 void Initialize(){ 114 scanf("%d%d%d",&m,&n,&k); 115 for(int i=1;i<=m;i++){ 116 scanf("%d",l+i); 117 Insert(0,i,l[i]); 118 sum+=l[i]; 119 } 120 for(int i=1;i<=n;i++){ 121 scanf("%d",c+i); 122 Insert(i+m,n+m+1,c[i]); 123 sum+=c[i]; 124 } 125 int a,b; 126 for(int i=0;i<k;i++){ 127 scanf("%d%d",&a,&b); 128 flag[a][b]=true; 129 } 130 } 131 132 bool Check(){ 133 int cnt; 134 for(int i=1;i<=m;i++){ 135 for(int j=1;j<=n;j++){ 136 if(!flag[i][j]) 137 cnt++; 138 } 139 if(cnt<l[i]) 140 return false; 141 } 142 for(int j=1;j<=n;j++){ 143 for(int i=1;i<=m;i++){ 144 if(!flag[i][j]) 145 cnt++; 146 } 147 if(cnt<c[j]) 148 return false; 149 } 150 return true; 151 } 152 153 inline void Insert(int a,int b,int flow){ 154 top->from=a; 155 top->to=b; 156 top->flow=flow; 157 top->next=head[a]; 158 head[a]=top; 159 top->rev=top+1; 160 top++; 161 top->from=b; 162 top->to=a; 163 top->flow=0; 164 top->next=head[b]; 165 head[b]=top; 166 top->rev=top-1; 167 top++; 168 }
本博客已弃用, 新个人主页: https://rvalue.moe, 新博客: https://blog.rvalue.moe