http://poj.org/problem?id=3308
题意:
大意是在一个n*m的矩阵中分布着一些点,可以通过一定费用消灭某行或某列的点,求消灭所有点所需的最小总费用,总费用为单次费用之积。
思路:
因为总费用是单次费用之积,可以利用对数的性质,log(xy) = logx + logy将问题由乘法变成加法,先对原题的目标代价求对数。
构建模型:以行和列看做点集,有敌人的行列相连。令X = {行集合},Y={列集合},E= {log(eij) | 火星伞兵将空投在第i行第j列 };这样就构造一个带权二分图G= {X,Y, E}。本题是二分图中的一个顶点覆盖问题。
求二分图的顶点覆盖问题,通常转化为最小割来求。即建立源点S 和汇点 T,假设二分图两个点集分别为 X 和 Y。X和Y原来的边容量设为INF,将S到X里每个点x连一条边,边权为x的点权,也可以看做覆盖点x的所有出边的花费(W-),将Y里每个点y到T连一条边,边权为y的点权,也可以看做覆盖点y的所有入边的花费(W+)。这样求得最小割容量,即为原问题的解。
而图G的最小割的容量,等于其最大流的流量,因此本题最终是转化为最大流问题求解。Dinic算法,特别注意加边的时候加反向边。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
const int maxm = 5050;
const int maxn = 5050;
const int INF = 0x3f3f;
struct node
{
int u,v;
double w;
int next;
}edge[maxm];
int cnt,head[maxm];
int s,t;
int n,m,l;
void add(int u, int v, double w)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
cnt++;
edge[cnt].u = v;
edge[cnt].v = u;
edge[cnt].w = 0;
edge[cnt].next = head[v];
head[v] = cnt;
cnt++;
}
int vis[maxn],dist[maxn];
queue <int> que;
void bfs()
{
memset(dist,0,sizeof(dist));
while(!que.empty()) que.pop();
vis[s] = 1;
que.push(s);
while(!que.empty())
{
int u = que.front();
que.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(edge[i].w && !vis[v])
{
que.push(v);
vis[v] = 1;
dist[v] = dist[u]+1;
}
}
}
}
double dfs(int u, double delta)
{
if(u == t) return delta;
else
{
double ret = 0;
for(int i = head[u]; i != -1 && delta; i = edge[i].next)
{
if(edge[i].w && dist[edge[i].v] == dist[u]+1)
{
double dd = dfs(edge[i].v, min(delta,edge[i].w));
edge[i].w -= dd;
edge[i^1].w += dd;
delta -= dd;
ret += dd;
}
}
return ret;
}
}
double Dinic()
{
double ret = 0;
while(true)
{
memset(vis,0,sizeof(vis));
bfs();
if(!vis[t]) return ret;
ret += dfs(s,INF);
}
}
int main()
{
int test;
double w;
int x,y;
scanf("%d",&test);
while(test--)
{
scanf("%d %d %d",&n,&m,&l);
s = 0; //超级源点
t = n+m+1; //超级汇点
memset(head,-1,sizeof(head));
cnt = 0;
for(int i = 1; i <= n; i++)
{
scanf("%lf",&w);
add(s,i,log(w)); //从S到X集合,先取对数建边
}
for(int i = 1+n; i <= m+n; i++)
{
scanf("%lf",&w);
add(i,t,log(w)); //从Y到T集合,先取对数建边
}
for(int i = 0; i < l; i++)
{
scanf("%d %d",&x,&y);
add(x,y+n,INF); //X到Y建边,流量INF
}
double ans;
ans = Dinic();
printf("%.4f\n",exp(ans));
}
return 0;
}
原文:http://blog.csdn.net/u013081425/article/details/18457165