【POJ】【3308】Paratroopers
2015-03-13 12:01
239 查看
网络流/二分图最小点权覆盖
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……
Source Code Problem: 3308 User: sdfzyhy Memory: 760K Time: 0MS Language: G++ Result: Accepted Source Code //POJ 3308 #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define rep(i,n) for(int i=0;i<n;++i) #define F(i,j,n) for(int i=j;i<=n;++i) #define D(i,j,n) for(int i=j;i>=n;--i) #define pb push_back using namespace std; inline int getint(){ int v=0,sign=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} return v*sign; } const int N=110,M=510,INF=1000000; const double eps=1e-8; typedef long long LL; /******************tamplate*********************/ int n,m,l; double r ,c ; struct edge{ int from,to; double cap,flow; }; struct Net{ edge E[M*3]; int head ,next[M*3],cnt; int Q ; int s,t,d ,cur ; inline void add(int from,int to,double cap){ E[++cnt]=(edge){from,to,cap,0}; next[cnt]=head[from]; head[from]=cnt; E[++cnt]=(edge){to,from,0,0}; next[cnt]=head[to]; head[to]=cnt; } inline void init(){ cnt=1; n=getint();m=getint();l=getint(); s=0; t=n+m+1; F(i,s,t) head[i]=0; F(i,1,n){ scanf("%lf",&r[i]); add(s,i,log(r[i])); } F(i,1,m){ scanf("%lf",&c[i]); add(i+n,t,log(c[i])); } int x,y; F(i,1,l){ x=getint(); y=getint(); add(x,y+n,INF); } } bool mklevel(){ memset(d,-1,sizeof d); d[s]=0; int l=0,r=-1; Q[++r]=s; while(l<=r){ int x=Q[l++]; for(int i=head[x];i;i=next[i]){ edge&e=E[i]; if (d[e.to]==-1 && e.cap>e.flow){ d[e.to]=d[x]+1; Q[++r]=(e.to); } } } return d[t]!=-1; } double dfs(int x,double a){ if (x==t) return a; double flow=0; for(int &i=cur[x];i && flow<a;i=next[i]){ edge&e=E[i]; if (e.cap-e.flow>0 && d[e.to]==d[x]+1){ double f=dfs(e.to,min(a-flow,e.cap-e.flow)); flow+=f; e.flow+=f; E[i^1].flow-=f; } } if (!flow) d[x]=-2; return flow; } double Dinic(){ double flow=0; while(mklevel()){ F(i,s,t) cur[i]=head[i]; flow+=dfs(s,INF); } return flow; } }G1; int main(){ #ifndef ONLINE_JUDGE freopen("3308.in","r",stdin); freopen("3308.out","w",stdout); #endif int T=getint(); while(T--){ G1.init(); printf("%.4f\n",exp(G1.Dinic())); } return 0; }
View Code
相关文章推荐
- POJ 3308 Paratroopers (对数转换+最小点权覆盖)
- POJ 3308 Paratroopers(最小割+Dinic)
- POJ 3308 Paratroopers(最小点权覆盖+dinic算法)
- POJ 3308 Paratroopers 已被翻译
- POJ 3308 Paratroopers
- POJ 3308 Paratroopers
- zoj 2874 & poj 3308 Paratroopers (最小割)
- poj3308 Paratroopers --- 最小点权覆盖->最小割
- POJ 3308 Paratroopers
- POJ 3308 Paratroopers(最小割+对数降级)
- POJ 3308 Paratroopers(最小割/KM)
- POJ3308_Paratroopers(最大流最小割)
- poj 3308 Paratroopers 最小割 最小点权覆盖
- 【最小割】POJ-3308 Paratroopers
- poj 3308 Paratroopers(网络流 最小割 dinic模板)
- POJ 3308: Paratroopers
- poj 3308 Paratroopers
- poj3308 Paratroopers --- 最小点权覆盖->最小割
- poj_3308 Paratroopers(二分图最小点权覆盖集+dinic+化乘为加)
- POJ 3308 Paratroopers(最小割EK(邻接表&矩阵))