ZOJ@2874題意:給一個n*m的網格,網格中某些位有敵人,現在可以在網格邊上的x軸或者y軸設置炮臺,x炮臺可以消滅所在列上的所有敵人,y炮臺可以消滅所在行上的所有敵人。現在給定修建x炮臺,y炮臺的費用,問如何設置炮臺,使得消滅所有敵人總費用最少(總費用為設置炮臺費用之積)。
解法:最大流。首先對炮臺費用求對數,使得總費用之積轉化為之和。建圖:從源點連有向邊到x軸,邊的容量為在該處設置炮臺的費用;y軸上的點連有向邊到匯點,邊容量為在該點設置炮臺的費用;如果網格中點(x,y)處有敵人,則從x處連邊到y,邊的容量設置為無窮大。
Code
// 2389416 2011-01-19 19:57:39 Accepted 2874 C++ 40 352 redsea
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
#define MAXN 105
#define inf 1000000000.00
double map[MAXN][MAXN];
double flow[MAXN][MAXN];
double max_flow(int n,double mat[][MAXN],int source,int sink,double flow[][MAXN]){
int pre[MAXN],que[MAXN],p,q,t,i,j;
double d[MAXN];
double tmp;
if (source==sink) return inf;
for (i=0;i<n;i++)
for (j=0;j<n;flow[i][j++]=0);
for (;;){
for (i=0;i<n;pre[i++]=0);
pre[t=source]=source+1,d[t]=inf;
for (p=q=0;p<=q&&!pre[sink];t=que[p++])
for (i=0;i<n;i++)
if (!pre[i]&&(tmp=mat[t][i]-flow[t][i]))
pre[que[q++]=i]=t+1,d[i]=d[t]<tmp?d[t]:tmp;
else if (!pre[i]&&(tmp=flow[i][t]))
pre[que[q++]=i]=-t-1,d[i]=d[t]<tmp?d[t]:tmp;
if (!pre[sink]) break;
for (i=sink;i!=source;)
if (pre[i]>0)
flow[pre[i]-1][i]+=d[sink],i=pre[i]-1;
else
flow[i][-pre[i]-1]-=d[sink],i=-pre[i]-1;
}
tmp = 0;
for (i=0;i<n;tmp+=flow[source][i++]);
return tmp;
}
int main()
{
int T, n, m, l, s, t;
double x;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&m,&n,&l);
s = 0;
t = m+n+1;
for(int i = 0; i < n+m+2; i++)
for(int j = 0; j < n+m+2; j++)
map[i][j] = 0;
for(int i = 1; i <= m; i++){
scanf("%lf",&x);
map[s][i] = log10(x);
}
for(int i = m+1; i <= n+m; i++){
scanf("%lf",&x);
map[i][t] = log10(x);
}
for(int i = 0; i < l; i++){
int sx, sy;
scanf("%d%d",&sx,&sy);
map[sx][m+sy] = 10;
}
printf("%.4lf\n", pow(10.0, max_flow(n+m+2,map,s,t,flow)));
}
return 0;
}