Luogu P3324 [SDOI2015]星际战争
二分+最大流
首先考虑二分答案
然后可以发现对于已知时间,判断是否可以将所有机器人摧毁可以用网络流
建立源点和汇点,源点向每一个激光武器连一条容量为$time*b[i]$的边,表示该激光武器在$time$时间下最多能产生的伤害为$time*b[i]$
每一个机器人向汇点连一条容量为$a[i]$的边,表示每一个机器人最多能承受的伤害
中间每一个激光武器向其能攻击的机器人连一条容量为$inf$的边
然后跑最大流,若可以跑出$\sum a[i]$,那么该时间可以将所有与机器人摧毁,更新二分边界
1 #include <bits/stdc++.h> 2 #define inf 1e9 3 using namespace std; 4 const int N=210,M=6100; 5 int n,m,s,t,tot,first[N],nxt[M],point[M],nrl[N],d[N]; 6 int vi[N][N]; 7 double data[M],a[N],b[N],sum; 8 void add_edge(int x,int y,double z) 9 { 10 tot++; 11 nxt[tot]=first[x]; 12 first[x]=tot; 13 point[tot]=y; 14 data[tot]=z; 15 tot++; 16 nxt[tot]=first[y]; 17 first[y]=tot; 18 point[tot]=x; 19 data[tot]=0; 20 } 21 bool bfs() 22 { 23 queue <int> q; 24 for (int i=s;i<=t;i++) 25 { 26 d[i]=inf; 27 nrl[i]=first[i]; 28 } 29 d[s]=0;q.push(s); 30 while (!q.empty()) 31 { 32 int x=q.front(); 33 q.pop(); 34 for (int i=first[x];i!=-1;i=nxt[i]) 35 { 36 int u=point[i]; 37 if (data[i]>0 && d[u]>d[x]+1) 38 { 39 d[u]=d[x]+1; 40 if (u==t) return true; 41 q.push(u); 42 } 43 } 44 } 45 return false; 46 } 47 double dfs(int x,double flow) 48 { 49 if (x==t) return flow; 50 double sum=0; 51 for (int i=nrl[x];i!=-1;i=nxt[i]) 52 { 53 int u=point[i]; 54 if (data[i]>0 && d[u]==d[x]+1) 55 { 56 double tmp=dfs(u,min(flow,data[i])); 57 flow-=tmp;sum+=tmp; 58 data[i]-=tmp;data[i^1]+=tmp; 59 if (flow<=0) return sum; 60 } 61 nrl[x]=nxt[i]; 62 } 63 return sum; 64 }//使用dinic跑最大流 65 bool check(double mid) 66 { 67 tot=-1; 68 memset(first,-1,sizeof(first)); 69 memset(nxt,-1,sizeof(nxt)); 70 s=0;t=n+m+1;//同上建边 71 for (int i=1;i<=m;i++) add_edge(s,i,mid*b[i]); 72 for (int i=1;i<=n;i++) add_edge(m+i,t,a[i]); 73 for (int i=1;i<=m;i++) 74 { 75 for (int j=1;j<=n;j++) 76 { 77 if (vi[i][j]) add_edge(i,m+j,inf); 78 } 79 } 80 double ans=0; 81 while (bfs()) ans+=dfs(s,inf); 82 return ans==sum;//判断是否跑满 83 } 84 int main() 85 { 86 scanf("%d%d",&n,&m); 87 for (int i=1;i<=n;i++) scanf("%lf",&a[i]); 88 for (int i=1;i<=m;i++) scanf("%lf",&b[i]); 89 for (int i=1;i<=m;i++) 90 { 91 for (int j=1;j<=n;j++) 92 scanf("%d",&vi[i][j]); 93 } 94 sum=0; 95 for (int i=1;i<=n;i++) sum+=a[i]; 96 double l=0,r=1e8; 97 while ((r-l)>=1e-8) 98 { 99 double mid=(l+r)/2; 100 if (check(mid)) r=mid; 101 else l=mid; 102 } 103 printf("%.6lf\n",r); 104 }