题意:
一个m*n大小的网格,已知伞兵着陆的具体位置(行和列)。现在在某行(或某列)
安装一架激光枪,一架激光枪能杀死该行(或该列)所有的伞兵。在第i行安装一架
激光枪的费用是Ri,在第i列安装的费用是Ci。要安装整个激光枪系统,总费用为这些
激光枪费用的乘积。
求杀死所有伞兵的最小费用。
构图:
把伞兵视为边,行与列视为顶点。增加源点和汇点,对于第i行,从源点向顶点i连接一条
容量为Ri的边。对于第j列,从顶点j向汇点连接一条容量为Rj的边。
如果某一点(i,j)有伞兵降落,则从顶点Ri向顶点Cj连接一条容量为无穷大的边。
算法:
根据割的性质,源点和汇点必不连通,则割边必定在S->R,R->C,C->T其一。为了求得最小容量,
将R->C设为无穷大,则其不可能被选中。这样割边集为S-->R,C-->T的集合,也就是选中行或列。
此时求得的最小割为花费最小的方案。
由于花费为行和列的乘积,则通过对数运算把乘法转化为加法。
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<queue> #define maxm 15000 #define maxn 105 #define eps 1e-6 using namespace std; struct node { int v,next; double val; }e[maxm<<1]; int st,en,n,m,l,cnt; int d[maxn]; int head[maxn],cur[maxn]; const double INF = 1000007; queue<int> q; void init() { st = 0,en = n+m+1; memset(head,-1,sizeof(head)); cnt = 0; } void add(int x,int y,double z) { e[cnt].v = y; e[cnt].val = z; e[cnt].next = head[x]; head[x]=cnt++; e[cnt].v = x; e[cnt].val = 0; e[cnt].next = head[y]; head[y]=cnt++; } bool bfs() { while(!q.empty()) q.pop(); memset(d,-1,sizeof(d)); int u; d[st] = 0; q.push(st); while(!q.empty()) { u = q.front(); q.pop(); for(int i=head[u];i!=-1;i=e[i].next) { int t = e[i].v; if(e[i].val>0 && d[t]==-1) { d[t] = d[u]+1; q.push(t); if(t==en) return true; } } } return false; } double dfs(int x,double flow) { if(x==en || fabs(flow)<=eps) return flow; double ret = 0,dd; for(int& i=cur[x];i!=-1;i=e[i].next) { int t = e[i].v; if(d[t] == d[x]+1 && (dd = dfs(t,min(flow,e[i].val)))>0) { e[i].val-=dd; e[i^1].val+=dd; flow-=dd; ret+=dd; if (fabs(flow) <= eps) break; } } return ret; } double Dinic() { double tmp = 0,maxflow = 0; while(bfs()) { for(int i=0;i<=en;i++) cur[i] = head[i]; maxflow+=dfs(st,INF); } return maxflow; } int main() { int T,a,b; double x; scanf("%d",&T); while(T--) { scanf("%d%d%d",&m,&n,&l); init(); for(int i=1;i<=m;i++) { scanf("%lf",&x); add(st,i,log(x)); } for(int i=1;i<=n;i++) { scanf("%lf",&x); add(i+m,en,log(x)); } for(int i=1;i<=l;i++) { scanf("%d%d",&a,&b); add(a,b+m,INF); } printf("%.4f\n",exp(Dinic())); } return 0; }
zoj 2874 & poj 3308 Paratroopers (最小割)
时间: 2024-11-11 05:14:44