题意:
火星人侵略地球,他们意图登陆破坏某个地区的兵器工厂。据探子回报,火星人登陆的地区为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 }