题目描述
传送门
题解
对于1..n1中的每个点都要最小化n到该点经过的所有道路的
∑ti∑si
这个应该是01分数规划问题,二分答案,使边权变成
ti−mid∗si
,判断图中是否有n到该点的权值为负的路径,有的话说明答案还可以再小。
那么对于每个点求出
val[x]=min{∑ti∑si}
,然后建立最小割的模型。
i为奇数:S->i 容量为val[i]
i为偶数:i->T 容量为val[i]
如果两个点之间有空腔:x->y 容量为inf
这样至少会割掉两个端点中的一个,保证每条空腔都被覆盖。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define N 1000003
#define eps 1e-5
using namespace std;
const double inf=1e12;
int point[N],nxt[N],v[N],S,T,can[N],n,m,n1,m1,tot,mark;
double a[N],b[N],dis[N],val[N];
struct data{
int point[N],nxt[N],v[N],cur[N],tot,deep[N];
double remain[N];
void add(int x,int y,double z){
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
// cout<<x<<" "<<y<<" "<<z<<endl;
}
void init() {
tot=-1;
memset(point,-1,sizeof(point));
S=n1+1; T=S+1;
}
void build(){
for (int i=1;i<=n1;i++)
if (i&1) add(S,i,val[i]);
else add(i,T,val[i]);
for (int i=1;i<=m1;i++) {
int x,y; scanf("%d%d",&x,&y);
if (val[x]==inf&&val[y]==inf) {
printf("-1\n");
mark=1; break;
}
add(x,y,inf);
}
}
int dcmp(double x) {
if (fabs(x)<eps) return 0;
return 1;
}
bool bfs(int s,int t){
for (int i=1;i<=t;i++) deep[i]=t+1;
for (int i=1;i<=t;i++) cur[i]=point[i];
queue<int> p; p.push(s); deep[s]=0;
while (!p.empty()) {
int now=p.front(); p.pop();
for (int i=point[now];i!=-1;i=nxt[i])
if (deep[v[i]]==t+1&&dcmp(remain[i]))
deep[v[i]]=deep[now]+1,p.push(v[i]);
}
if (deep[t]==t+1) return false;
return true;
}
double dfs(int now,int t,double limit){
if (now==t||!dcmp(limit)) return limit;
double flow=0,f;
for (int i=cur[now];i!=-1;i=nxt[i]){
cur[now]=i;
if (deep[v[i]]==deep[now]+1&&(f=dfs(v[i],t,min(remain[i],limit)))) {
flow+=f; limit-=f;
remain[i]-=f; remain[i^1]+=f;
if (!dcmp(limit)) break;
}
}
return flow;
}
double dinic(int s,int t){
double ans=0;
while (bfs(s,t)) ans+=dfs(s,t,inf);
return ans;
}
}E;
void add(int x,int y,double a1,double b1)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; a[tot]=a1; b[tot]=b1;
}
bool check(double x,int T)
{
for (int i=1;i<=n;i++) can[i]=0,dis[i]=inf;
dis[n]=0; can[n]=1;
queue<int> p; p.push(n);
while (!p.empty()) {
int now=p.front(); p.pop();
for (int i=point[now];i;i=nxt[i]) {
double k=a[i]-b[i]*x;
if (dis[v[i]]>dis[now]+k) {
dis[v[i]]=dis[now]+k;
if (dis[v[i]]<=0&&v[i]==T) return 1;
if (!can[v[i]]) {
can[v[i]]=1;
p.push(v[i]);
}
}
}
can[now]=0;
}
return dis[T]<=0;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m); double sum=0;
for (int i=1;i<=m;i++) {
int x,y; double s,t;
scanf("%d%d%lf%lf",&x,&y,&s,&t);
add(x,y,s,t); sum+=s;
}
scanf("%d%d",&m1,&n1);
for (int i=1;i<=n1;i++) {
double l=0; double r=10.0; double ans=sum;
while (r-l>=eps) {
double mid=(l+r)/2;
if (check(mid,i)) r=mid-eps,ans=min(ans,mid);
else l=mid+eps;
}
if (dis[i]==inf) val[i]=inf;
else val[i]=ans; //printf("%.3lf ",val[i]);
}
// cout<<endl;
E.init(); E.build();
if(mark) return 0;
printf("%.1lf\n",E.dinic(S,T));
}