网络流/二分图最小点权覆盖
sigh……这题……TLE&RE了好几发
建一个二分图,左边的每个结点代表行,右边的代表列,如果在(i,j)这个位置有一个外星人,那么我们就连一条边 (左 i ->右 j),这样的话,求一个二分图最小点权覆盖即可。
为什么这样建图是对的?大家学过二分图应该知道点覆盖是啥吧……(至少学过最小点覆盖=最大匹配吧?)那么我们选了某一个点,比如我们选了左边的第 i 个点,这就表示我们在第 i 行建了一座激光炮,它能打到的外星人就是这个点连出去的所有边!也就是说对于每个外星人(每条边),它被打死(边被覆盖)当且仅当它所在行或列至少有一个方向有激光炮(它连接的两个端点至少有一个被选中),那么现在应该明白了吧= =我们就是要选出权值和最小的一组点,使得所有边都被覆盖。这个用网络流来做比较方便……(其实是我不会别的做法……)
那么我们现在知道了这是一个二分图的模型,那么怎么建网络流的图呢?对于每个左边的顶点,我们连s->i,w=r[i],右边顶点连汇点t,边权(流量)同理。然后对于二分图中的边我们连(左->右,流量为INF),跑一个最大流(最小割)。(割掉某条边就表示选这个点,边权为INF表示不割它)
看到这里你可能会问,这样做是不对的吧?样例都过不了的……
其实……一开始我由于英语没学好……“product”的意思是乘积!!!也就是所有建造的激光炮的权值之积……sigh
辣么怎么算积的最大值呢?网络流不是只能算和的最大值吗?下面就是见证奇迹的时刻:对所有的权值取log,然后对最终答案取exp!这样就华丽丽地将求乘积最大转化为了求和最大。
Trick:这题是取了log的边权……所以在用Dinic增广的时候边界判定需要小心小心再小心。对于double类型的最大流我参考网上的代码重新写了一个模板……
之前在【当前结点是否无法再继续增广(dfs时)】这个地方蛋疼了好久……因为边界处理的不好各种TLE
然后是边集开小了……500×2的RE,500×3的AC……
1 Source Code 2 Problem: 3308 User: sdfzyhy 3 Memory: 760K Time: 0MS 4 Language: G++ Result: Accepted 5 6 Source Code 7 8 //POJ 3308 9 #include<cmath> 10 #include<queue> 11 #include<vector> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 inline int getint(){ 23 int v=0,sign=1; char ch=getchar(); 24 while(ch<‘0‘||ch>‘9‘){ if (ch==‘-‘) sign=-1; ch=getchar();} 25 while(ch>=‘0‘&&ch<=‘9‘){ v=v*10+ch-‘0‘; ch=getchar();} 26 return v*sign; 27 } 28 const int N=110,M=510,INF=1000000; 29 const double eps=1e-8; 30 typedef long long LL; 31 /******************tamplate*********************/ 32 33 int n,m,l; 34 double r[N],c[N]; 35 struct edge{ 36 int from,to; 37 double cap,flow; 38 }; 39 struct Net{ 40 edge E[M*3]; 41 int head[N],next[M*3],cnt; 42 int Q[N]; 43 int s,t,d[N],cur[N]; 44 inline void add(int from,int to,double cap){ 45 E[++cnt]=(edge){from,to,cap,0}; 46 next[cnt]=head[from]; 47 head[from]=cnt; 48 E[++cnt]=(edge){to,from,0,0}; 49 next[cnt]=head[to]; 50 head[to]=cnt; 51 } 52 inline void init(){ 53 cnt=1; 54 n=getint();m=getint();l=getint(); 55 s=0; t=n+m+1; 56 F(i,s,t) head[i]=0; 57 F(i,1,n){ 58 scanf("%lf",&r[i]); 59 add(s,i,log(r[i])); 60 } 61 F(i,1,m){ 62 scanf("%lf",&c[i]); 63 add(i+n,t,log(c[i])); 64 } 65 int x,y; 66 F(i,1,l){ 67 x=getint(); y=getint(); 68 add(x,y+n,INF); 69 } 70 } 71 bool mklevel(){ 72 memset(d,-1,sizeof d); 73 d[s]=0; 74 int l=0,r=-1; 75 Q[++r]=s; 76 while(l<=r){ 77 int x=Q[l++]; 78 for(int i=head[x];i;i=next[i]){ 79 edge&e=E[i]; 80 if (d[e.to]==-1 && e.cap>e.flow){ 81 d[e.to]=d[x]+1; 82 Q[++r]=(e.to); 83 } 84 } 85 } 86 return d[t]!=-1; 87 } 88 double dfs(int x,double a){ 89 if (x==t) return a; 90 double flow=0; 91 for(int &i=cur[x];i && flow<a;i=next[i]){ 92 edge&e=E[i]; 93 if (e.cap-e.flow>0 && d[e.to]==d[x]+1){ 94 double f=dfs(e.to,min(a-flow,e.cap-e.flow)); 95 flow+=f; 96 e.flow+=f; 97 E[i^1].flow-=f; 98 } 99 } 100 if (!flow) d[x]=-2; 101 return flow; 102 } 103 double Dinic(){ 104 double flow=0; 105 while(mklevel()){ 106 F(i,s,t) cur[i]=head[i]; 107 flow+=dfs(s,INF); 108 } 109 return flow; 110 } 111 }G1; 112 113 int main(){ 114 #ifndef ONLINE_JUDGE 115 freopen("3308.in","r",stdin); 116 freopen("3308.out","w",stdout); 117 #endif 118 int T=getint(); 119 while(T--){ 120 G1.init(); 121 printf("%.4f\n",exp(G1.Dinic())); 122 } 123 return 0; 124 }