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 }

 

你可能感兴趣的:(poj)