题意:n*m棋盘上有若干伞兵,现欲在若干行或若干列上建立武器(i行上的武器能干掉i行上所有的伞兵,列同理),给出在每行(列)上的建造费用,已知总费用为所有武器的费用的乘积。求干掉所有士兵的最小费用。(3041也就是3308的简化版,每个武器费用为1,直接求最小点覆盖即可)
思路:行和列分别作为二部图的一部,有伞兵在i行j列上则(i,j)有边。如果这个伞兵被干掉,那么i行或者j列必选一个。也就转化成了求二部图的最小点权覆盖。还是转化为最大流(最小割)来求。因为总费用是所有费用的乘积,所以与源点或者汇点相连的边的权值要取对数,最后取指数为答案。
#include <stdio.h>
#include <string.h>
#include <math.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define INF 0x3fffffff
#define N 110
int T,n,m,k;
double f[N][N],a[N],q[N];
int p[N];
double maxflow(int s,int t){
int i;
double res=0;
while(1){
int front,rear,now;
front = rear = -1;
q[++rear] = s;
memset(a,0,sizeof(a));
a[s] = INF;
memset(p,0,sizeof(p));
while(front < rear){
now = q[++front];
for(i = s;i<=t;i++)
if(!a[i] && min(f[now][i],a[now]) > 0){
a[i] = min(f[now][i],a[now]);
p[i] = now;
q[++rear] = i;
}
}
if(!a[t])
break;
res += a[t];
for(i = t;i!=s;i=p[i]){
f[p[i]][i] -= a[t];
f[i][p[i]] += a[t];
}
}
return res;
}
int main(){
scanf("%d",&T);
while(T--){
int i,j,w;
double a;
memset(f,0,sizeof(f));
scanf("%d %d %d",&n,&m,&k);
for(i = 1;i<=n;i++){
scanf("%lf",&a);
f[0][i] = log(a)/log(2.);
}
for(i = 1;i<=m;i++){
scanf("%lf",&a);
f[n+i][n+m+1] = log(a)/log(2.);
}
for(i = 0;i<k;i++){
scanf("%d %d",&j,&w);
f[j][n+w] = INF;
}
printf("%.4lf\n",pow(2,maxflow(0,n+m+1)));
}
return 0;
}
3041:
#include <stdio.h>
#include <string.h>
#define N 505
struct edge{
int y,next;
}e[10005];
int lk[N],used[N],n,m,first[N],top;
void add(int x,int y){
e[top].y = y;
e[top].next = first[x];
first[x] = top++;
}
int dfs(int x){
int i,y;
for(i = first[x];i!=-1;i=e[i].next){
y = e[i].y;
if(!used[y]){
used[y] = 1;
if(lk[y]==-1 || dfs(lk[y])){
lk[y] = x;
return 1;
}
}
}
return 0;
}
int hungary(){
int i,res=0;
for(i = 1;i<=n;i++){
memset(used, 0, sizeof(used));
if(dfs(i))
res++;
}
return res;
}
int main(){
while (scanf("%d %d",&n,&m)!=EOF) {
int i,a,b;
memset(first, -1, sizeof(first));
memset(lk, -1, sizeof(lk));
top = 0;
for(i = 0;i<m;i++){
scanf("%d %d",&a,&b);
add(a,b);
}
printf("%d\n",hungary());
}
return 0;
}