POJ 3308(最小割最大流)

题意:

火星人侵略地球,他们意图登陆破坏某个地区的兵器工厂。据探子回报,火星人登陆的地区为n*m大小的地域,而且每一个火星人的着陆点坐标已知。

火星人很强悍,只要有一个火星人着陆后能够幸存,他必定能毁坏这片区域的全部兵工厂。为了防止这种情况发生,必须保证在火星人着陆的一瞬间把他们全部同时杀死。

现在防卫队有一个激光枪,开一枪就能把 在同一行(或同一列)着陆的火星人全部杀死。但是这种激光枪的使用是有代价的,把这种激光枪安装到不同行的行首、或者不同列的列首,费用都不同。现在已知把激光枪安装到任意位置的费用,总的花费为这些安装了激光枪的行列花费的乘积。

问怎样安装激光枪才能在杀死所有火星人的前提下费用最少?

题解:

这个题是很裸的二分图的最小顶点覆盖,这种问题一般转化成网络流的最小割。

 

这道题要求的是乘积,我们应该马上想到log取对数。(与之类似的题:HDU 3666,也是很经典的题)

 

然后就是构图了:

设S为超级源点,T为超级汇点

S与每行连一条容量为log10(row[i])的点(row[i]是在i行建激光炮的花费)

T与每列连一条容量为log10(col[i])的点(col[i]是在i列建激光炮的花费)

对于每一个火星人降落的位置(x,y)连一条从x到y容量为INF的边,意味着这条表永远不会被割断

 

图就建好了,跑最大流就行了!

 

View Code
 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <iostream>
 4 #include <cstring>
 5 #include <cmath>
 6 #define N 201000
 7 #define M  2001000
 8 #define INF 100.0
 9 using namespace std;
10 int head[N],next[M],to[M],cnt,S,T,n,m,num,layer[N],q[M<<4],tt;
11 double len[M],row[N],col[N];
12 inline void add(int u,int v,double w)
13 {
14     to[cnt]=v; len[cnt]=w; next[cnt]=head[u]; head[u]=cnt++;
15     to[cnt]=u; len[cnt]=0.0; next[cnt]=head[v]; head[v]=cnt++;
16 }
17 void read()
18 {
19     memset(head,-1,sizeof head);
20     cnt=0;
21     scanf("%d%d%d",&n,&m,&num);
22     S=0; T=n+m+1;
23     for(int i=1;i<=n;i++) scanf("%lf",&row[i]),add(S,i,log10(row[i]));
24     for(int i=1;i<=m;i++) scanf("%lf",&col[i]),add(i+n,T,log10(col[i]));
25     for(int i=1,a,b;i<=num;i++)
26     {
27         scanf("%d%d",&a,&b);
28         add(a,b+n,INF);
29     }
30 }
31 bool bfs()
32 {
33     memset(layer,-1,sizeof layer);
34     int h=1,t=2,sta;
35     q[1]=S; layer[S]=0;
36     while(h<t)
37     {
38         sta=q[h++];
39         for(int i=head[sta];~i;i=next[i])
40             if(len[i]>0.0&&layer[to[i]]<0)
41             {
42                 layer[to[i]]=layer[sta]+1;
43                 q[t++]=to[i];
44             }
45     }
46     return layer[T]!=-1;
47 }
48 double min(double a,double b)
49 {
50     if(a<b) return a;
51     else return b;
52 }
53 double find(int u,double cur_flow)
54 {
55     if(u==T) return cur_flow;
56     double result=0.0,tmp;
57     for(int i=head[u];~i&&result<cur_flow;i=next[i])
58         if(len[i]>0.0&&layer[to[i]]==layer[u]+1)
59         {
60             tmp=find(to[i],min(len[i],cur_flow-result));
61             len[i]-=tmp; len[i^1]+=tmp; result+=tmp;
62         }
63     if(result<=0.000001) layer[u]=-1;
64     return result;
65 }
66 void dinic()
67 {
68     double ans=0.0;
69     while(bfs()) ans+=find(S,INF);
70     ans=pow(10,ans);
71     printf("%.4lf\n",ans);
72 }
73 int main()
74 {
75     scanf("%d",&tt);
76     while(tt--)
77     {
78         read();
79         dinic();
80     }
81     system("pause");
82     return 0;
83 }

 

附HDU 3666代码(差分约束系统)

View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cmath> 
 6 #define N 2001
 7 #define M 400000
 8 using namespace std;
 9 int n,m,cnt,to[M],next[M],head[N],q[M<<3],num[N];
10 double dis[N],len[M],l,u; 
11 bool vis[N];
12 void add(int u,int v,double w)
13 {
14     to[cnt]=v; len[cnt]=w; next[cnt]=head[u]; head[u]=cnt++;
15 }
16 void read()
17 {
18     memset(dis,0x7f,sizeof dis); 
19     memset(head,0,sizeof head); 
20     memset(vis,0,sizeof vis); 
21     memset(num,0,sizeof num);
22     cnt=1; 
23     double ls; 
24     l=log(l); u=log(u); 
25     for(int i=1;i<=n;i++)
26         for(int j=1;j<=m;j++)
27         {
28             scanf("%lf",&ls);
29             ls=log(ls); 
30             add(n+j,i,u-ls); 
31             add(i,n+j,ls-l); 
32         } 
33 } 
34 bool spfa()
35 {
36     int h=1,t=2;
37     q[1]=1; vis[1]=true; dis[1]=0.0; num[1]=1; 
38     while(h<t)
39     {
40         int sta=q[h++];
41         vis[sta]=false;
42         for(int i=head[sta];i;i=next[i])
43         {
44             if(dis[to[i]]>dis[sta]+len[i])
45             {
46                 dis[to[i]]=dis[sta]+len[i];
47                 if(!vis[to[i]])
48                 {
49                     vis[to[i]]=true;
50                     q[t++]=to[i];
51                     num[to[i]]++; 
52                     if(num[to[i]]>(int)(sqrt((double)(n+m)))) return false; 
53                 } 
54             }
55         }
56     }
57     return true; 
58 } 
59 int main()
60 {
61     while(scanf("%d%d%lf%lf",&n,&m,&l,&u)!=EOF)
62     {
63         read();
64         if(spfa()) printf("YES\n");
65         else printf("NO\n"); 
66     } 
67     system("pause");
68     return 0;
69 }

 

posted @ 2013-01-08 21:46  proverbs  阅读(377)  评论(0编辑  收藏  举报