题意:
给定n个点有m1个军事基地要探索,每个军事基地有两个出入口u,v (u,v 属于1-n1点)只要到达一个出入口(1-n1) 就可以探索与其相连的所有的出入口, 问最少的代价探索所有的出入口
m 条有向边 ,形成DAG, 每条边有两个属性s,t,路径代价为 经过所有边的 s属性和/t属性和。起始点在n点。
题解:
-
主要可以转化为两个问题
- 求n到每个出入口的时间和与安全系数和比值最小的路径
- 求出探索所有空腔的最小代价
- 前者是一个分数规划问题,二分 加最短路验证即可
- 后者是一个最小割问题
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f #define eps 1e-8 const int N=1e5+10; const int M=1e5+10; struct Edge { int to, next;double w; } edge[M<<1]; int head[N],cur[N],pos=1,level[N]; void add(int a, int b, double c) { edge[++pos] = (Edge){b, head[a], c};head[a] = pos; edge[++pos] = (Edge){a, head[b], 0};head[b] = pos; } bool bfs(int s, int t) { memset(level, 0, sizeof level); queue<int> q; level[s] = 1; q.push(s); while (!q.empty()) { int pos = q.front();q.pop(); for (int i = head[pos]; i; i = edge[i].next) { int v = edge[i].to; if ( fabs(edge[i].w-0)<=eps || level[v]) continue; level[v] = level[pos] + 1; q.push(v); } } return level[t]; } double dfs(int s, int t, double flow) { if(s==t||fabs(flow-0)<=eps)return flow; double f,ret = 0; for (int i = cur[s],v; i; i = edge[i].next) { v = edge[i].to; if (level[v] == level[s] + 1 && (f=dfs(v,t,min(flow,edge[i].w)))>=eps) { edge[i].w -= f; edge[i ^ 1].w += f; flow -= f; ret += f; if(!flow)break; } } return ret; } double dinic(int s, int t) { double ret = 0; while (bfs(s, t)) memcpy(cur,head,sizeof cur),ret += dfs(s, t, 1e50); return ret; } struct Edge2{int to,nex;double t,s;}edge2[N<<1]; int head2[N],pos2; void addtp(int a,int b,double c,double d) { edge2[++pos2]=(Edge2){b,head2[a],c,d};head2[a]=pos2; } int n,m,s,t; double val[N]; int vis[N];double dis[N]; bool check(int s,int t,double x) { for(int i=1;i<=n;i++)dis[i]=1e50,vis[i]=0; vis[s]=1;dis[s]=0; queue<int>q;while(!q.empty())q.pop(); q.push(s); while(!q.empty()) { int u=q.front();q.pop();vis[u]=0; for(int i=head2[u];i;i=edge2[i].nex) { int v=edge2[i].to; if(dis[v]>dis[u]+edge2[i].t-edge2[i].s*x) { dis[v]=dis[u]+edge2[i].t-edge2[i].s*x; if(v==t&&dis[v]<eps)return 1; if(!vis[v])vis[v]=1,q.push(v); } } } return dis[t]<-eps; } int main() { cin>>n>>m;int a,b;double c,d; while(m--) { scanf("%d%d%lf%lf",&a,&b,&c,&d); addtp(a,b,c,d); } int n1,m1; cin>>m1>>n1; for(int i=1;i<=n1;i++) { double L=0,R=100,ans=0; while(L<R-1e-4) { double mid=(L+R)*0.5; if(check(n,i,mid))ans=mid,R=mid; else L=mid; } if(ans==0)val[i]=1e50; else val[i]=ans; } s=n1+1,t=s+1;int u,v; for(int i=1;i<=m1;i++) { scanf("%d%d",&u,&v); if(v&1)swap(u,v); if(val[u]==1e50&&val[v]==1e50)return printf("-1"),0; add(u,v,1e50); } for(int i=1;i<=n1;i++) if(i&1) add(s,i,val[i]); else add(i,t,val[i]); printf("%.1f",dinic(s,t)); }