题解 CF653D 【Delivery Bears】
-
CF653D Delivery Bears
比较特别的网络流题目。
就是无脑乱搞,相信自己就过了!
-
Prelude
这个题上手就感觉太神必了,根本感觉不是网络流。该用网络流你不用,不用的你一个劲想。
因为众所周知题面告诉你一个算法的那这个题肯定不用这个算法(雾)。
为啥要说这个,因为这个题上手一看:每条边有容量,有流量,求最大流量,这不是网络流吗?
但是其实感觉很难做,因为要求的流量限制太多,很难直接做。于是我们需要转换思路了。
-
Solution
这个题也没啥建模,直接讲吧。
就是说,我们考虑到对于一个要求的重量的限制只是边上的限制 \(c_i\) 。如果我们知道货物的统一重量 \(d\) ,那么我们就只需要考虑一条边上经过的熊的个数上限 \(\displaystyle\lfloor \frac{c_i}{d}\rfloor\) 。这样有显然的好处:
- 我们可以把运算转换成
int
类型的。 - 这样方便判断每只熊是否能到达。
这样就可以直接判了,因为把每只熊看作 \(1\) 的流量,那么每只熊 \(1\rightarrow n\) 一路走下去如果不溢出必然有 \(x\) 的流量。所以只需要判断能否有 \(x\) 流量到达汇点 \(n\) 就这个 \(d\) 满足条件。
那么 \(d\) 怎么确定呢,都说到这份上了,还不想想二分答案?
上面那个显然是 check(mid)
的思路,所以直接做二分答案,上界 r
最多也就 \(1e6\) 。
一看 \(n\leq 50\) ,显然可以过。
最后的答案是总重量,所以是 \(x\times ans\) 。
-
Code
实现判断直接用 \(\texttt{Dinic}\) 跑最大流就可以了,超过 \(x\) 即是合法的。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
//#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define INL inline
#define Re register
//Tosaka Rin Suki~
using namespace std;
INL int read()
{
int x=0;int w=1;
char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')
{x=(x<<1)+(x<<3)+ch-48,ch=getchar();}
return x*w;
}
INL int mx(int a,int b){return a>b?a:b;}
INL int mn(int a,int b){return a<b?a:b;}
const int INF=1<<30,N=1005,M=1000005;
int n,m,x,S,T;
struct Rey
{
int nxt,to,c;
}e[M];
int head[N],cnt;
int dis[N],cur[N];
int a[N],b[N],c[N];
INL void add_edge(int u,int v,int w)
{
e[++cnt].nxt=head[u];
e[cnt].to=v;
e[cnt].c=w;
head[u]=cnt;
}
INL void add_flow(int u,int v,int w)
{
add_edge(u,v,w);
add_edge(v,u,0);
}
INL bool bfs()
{
queue <int> q;
q.push(S);
memset(dis,0,sizeof(dis));
dis[S]=1;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt)
{
int go=e[i].to;
if(e[i].c>0&&!dis[go])
{
dis[go]=dis[u]+1;
q.push(go);
if(dis[T])return 1;
}
}
}
return dis[T]!=0;
}
INL int dfs(int x,int flow)
{
if(x==T||!flow)return flow;
int usd=0;
for(int i=cur[x];i;i=e[i].nxt)
{
cur[x]=i;
int go=e[i].to;
if(e[i].c>0&&dis[go]==dis[x]+1)
{
int C=dfs(go,min(flow-usd,e[i].c));
usd+=C;
e[i].c-=C,e[i^1].c+=C;
if(flow==0)return usd;
}
}
if(!usd)dis[x]=0;
return usd;
}
INL int Dinic()
{
int fl=0;
while(bfs())
{
memcpy(cur,head,sizeof(head));
fl+=dfs(S,INF);
}
return fl;
}
INL bool check(double p)
{
cnt=1;memset(head,0,sizeof(head));
for(int i=1;i<=m;i++)
{
int x=min(c[i]/p,1e9);//p 有时候会莫名其妙的很小或者一些溢出问题,所以特判。
add_flow(a[i],b[i],x);
}
return Dinic()>=x;
}
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=read(),m=read(),x=read();
S=1,T=n;
for(int i=1;i<=m;i++)
{
a[i]=read();b[i]=read(),c[i]=read();
}
double l=0,r=1e6;
for(int i=0;i<80;i++)//因为是 double 所以直接暴力循环多次,在复杂度能承受范围以及误差范围内即可。
{
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%.12lf\n",(double)l*x);
return 0;
}
博主已经退役啦,现在在家里睡大觉ZzzzzzzzzzzzZ