Sightseeing Cows
给定一个n(2 <= n <= 1000)个点,m(2 <= m <= 5000)条边的有向图,给定每个点的点值f(i)和每条边的权值w(i),求一个环使得路径上点权和除以边权和最大。
简单来说就是最优比率生成环。
题解
01分数规划问题,按套路二分答案。
现在问题转化为了判断可行。把边权变成\(mid*T-F\),这样可行的话一个环的和<0,问题转化成了判断负环。
而如何判断负环呢?可以记录入队次数,也可以记录最短路边数。而后一种效率更高。
时间复杂度\(O(nm\log v)\)
co int N=1e3+1,M=5e3+1;
co double eps=1e-6;
int n,m,c[N],f[N],x[M],y[M],z[M];
int Head[N],Edge[M],Next[M],tot;
double Leng[M],d[N];
bool v[N];
il void add(int x,int y,double z){
Edge[++tot]=y,Leng[tot]=z,Next[tot]=Head[x],Head[x]=tot;
}
bool judge(double w){
tot=0,memset(Head,0,sizeof Head);
for(int i=1;i<=m;++i) add(x[i],y[i],w*z[i]-f[x[i]]);
queue q;
for(int i=1;i<=n;++i) q.push(i),d[i]=0,v[i]=1;
memset(c,0,sizeof c);
while(q.size()){
int x=q.front();q.pop();
v[x]=0;
for(int i=Head[x];i;i=Next[i]){
int y=Edge[i];
if(d[y]>d[x]+Leng[i]){
d[y]=d[x]+Leng[i];
if((c[y]=c[x]+1)>=n) return 1;
if(!v[y]) q.push(y),v[y]=1;
}
}
}
return 0;
}
int main(){
read(n),read(m);
for(int i=1;i<=n;++i) read(f[i]);
for(int i=1;i<=m;++i) read(x[i]),read(y[i]),read(z[i]);
double l=0,r=1000;
while(r-l>eps){
double mid=(l+r)/2;
if(judge(mid)) l=mid;
else r=mid;
}
printf("%.2f",l);
return 0;
}
6B12 最优高铁环 0x6B「图论」练习
背景
幻影国建成了当今世界上最先进的高铁,该国高铁分为以下几类:
S---高速光子动力列车---时速1000km/h
G---高速动车---时速500km/h
D---动车组---时速300km/h
T---特快---时速200km/h
K---快速---时速150km/h
该国列车车次标号由上述字母开头,后面跟着一个正整数(<=1000)构成。
描述
由于该国地形起伏不平,各地铁路的适宜运行速度不同。因此该国的每一条行车路线都由K列车次构成。例如:K=5的一条路线为:T120-D135-S1-G12-K856。当某一条路线的末尾车次与另一条路线的开头车次相同时,这两条路线可以连接起来变为一条更长的行车路线。显然若干条路线连接起来有可能构成一个环。
若有3条行车路线分别为:
x1-x2-x3
x3-x4
x4-x5-x1
x1~x5车次的速度分别为v1~v5
定义高铁环的值为(环上各条行车路线速度和)的平均值,即:
[(v1+v2+v3)+(v3+v4)+(v4+v5+v1)]/3.
所有高铁环的值的最大值称为最优高铁环的值。
给出M条行车路线,求最优高铁环的值。
输入格式
第一行为行车路线条数M
接下来M行每行一条行车路线,由若干车次构成,各车次之间用'-'号隔开,车次的标号方式如上所述。
数据保证输入的合法性。
输出格式
最优高铁环的值。四舍五入到最接近的整数。
若不存在这样的环,输出-1.
样例输入
3 T120-D135-S1 S1-G12 G12-K856-T120
样例输出
1283
数据范围与约定
- 50%的数据:0
100%的数据,0 每条行车路线车次个数不超过20,数据保证结果不超过2^31-1。
样例解释
[(200+300+1000)+(1000+500)+(500+150+200)]/3=1283
题解
把每段行车路线的起点终点车次看成节点,则给出的就是一些边,然后要求一个01分数规划问题。
二分答案后,找负(正)环即可。
co int N=5e4+1;
co double eps=1e-3;
int n,m,st,ed,t;
int head[N],edge[N],next[N],tot;
double d[N],s[N],leng[N];
bool v[N];
il void add(int x,int y){
edge[++tot]=y,next[tot]=head[x],head[x]=tot;
}
il int f(char ch){
switch(ch){
case 'S': t=0;return 1000;
case 'G': t=1;return 500;
case 'D': t=2;return 300;
case 'T': t=3;return 200;
case 'K': t=4;return 150;
}
return -1;
}
bool dfs(int x){
v[x]=1;
for(int i=head[x];i;i=next[i]){
int y=edge[i];double z=leng[i];
if(d[y]>d[x]+z){
d[y]=d[x]+z;
if(v[y]||dfs(y)) return 1;
}
}
v[x]=0;
return 0;
}
bool pd(double x){
for(int i=1;i<=m;++i) leng[i]=x-s[i];
memset(v,0,sizeof v);
memset(d,0x3f,sizeof d);
for(int i=1;ieps){
double mid=(l+r)/2;
if(pd(mid)) l=ans=mid;
else r=mid;
}
printf("%.0lf\n",ans);
return 0;
}
方伯伯运椰子
四川的方伯伯为了致富,决定引进海南的椰子树。方伯伯的椰子园十分现代化,椰子园中有一套独特的交通系统。
现在用点来表示交通节点,边来表示道路。这样,方伯伯的椰子园就可以看作一个有 \(n + 2\) 个交通节点,\(m\) 条边的有向无环图。\(n +1\) 号点为入口,\(n +2\) 号点为出口。每条道路都有六个参数,\(u_i, v_i, a_i, b_i, c_i, d_i\),分别表示,该道路从 \(u_i\) 号点通向 \(v_i\) 号点,将它的容量压缩一次要 \(a_i\) 的花费,容量扩大一次要 \(b_i\) 的花费,该条道路当前的运输容量上限为 \(c_i\),并且每单位运输量通过该道路要 \(d_i\) 的费用。
在这个交通网络中,只有一条道路与起点相连。因为弄坏了这条道路就会导致整个交通网络瘫痪,聪明的方伯伯决定绝不对这条道路进行调整,也就是说,现在除了这条道路之外,对其余道路都可以进行调整。
有两种调整方式:
- 选择一条道路,将其进行一次压缩,这条道路的容量会下降 1 单位。
- 选择一条道路,将其进行一次扩容,这条道路的容量会上升 1 单位。
一条道路可以被多次调整。
由于很久以前,方伯伯就请过一个工程师,对这个交通网络进行过一次大的优化调整。所以现在所有的道路都被完全的利用起来了,即每条道路的负荷都是满的(每条道路的流量等于其容量)。但方伯伯一想到自己的海南椰子会大丰收,就十分担心巨大的运输量下,会导致过多的花费。因此,方伯伯决定至少进行一次调整,调整之后,必须要保持每条道路满负荷,且总交通量不会减少。
设调整后的总费用是 \(Y\),调整之前的总费用是 \(X\)。现在方伯伯想知道,最优调整比率是多少,即假设他进行了 \(k\) 次调整,\((X - Y)/k\) 最大能是多少?
注:总费用 = 交通网络的运输花费 + 调整的花费
对于所有数据,\(1 \leq N \leq 5000,\ 0 \leq M \leq 3000,\ 1 \leq U_i,V_i \leq N+2,\ 0 \leq A_i,B_i \leq 500,\ 0 \leq C_i \leq 10000,\ 0 \leq D_i \leq 1000\)。
题解
一个可以增容和减容的网络流图?
由于要满流,所以减容操作的花费是\(a_i-d_i\),增容操作的花费是\(b_i+d_i\)。
01分数规划,二分答案\(\text{mid}\)。
起点出边不能变,所以总流量不会变。那么增加的流量和减少的流量抵消了。
有向无环图,所以改的应该是个“环”?\(Y-X+k\cdot \text{mid}\leq 0\),所以找的应该是负环。
负环要把所有点都放进去……
CO int N=5e3+10;
CO float128 inf=1e18;
struct edge {int y,w;};
vector to[N];
float128 dis[N];
int vis[N],cnt[N];
int main(){
int n=read();
for(int m=read();m--;){
int x=read(),y=read();
int a=read(),b=read(),c=read(),d=read();
to[x].push_back({y,b+d});
if(c) to[y].push_back({x,a-d});
}
float128 L=0,R=1e9;
for(int t=1;t<=40;++t){
float128 mid=(L+R)/2;
function check=[&]()->bool{
deque que;
for(int i=1;i<=n+2;++i){
dis[i]=0,cnt[i]=0;
que.push_back(i),vis[i]=1;
}
while(que.size()){
int x=que.front();
que.pop_front(),vis[x]=0;
for(CO edge&e:to[x])if(dis[e.y]>dis[x]+e.w+mid){
dis[e.y]=dis[x]+e.w+mid;
if((cnt[e.y]=cnt[x]+1)>=n) return 1;
if(!vis[e.y]) que.push_back(e.y),vis[e.y]=1;
}
}
return 0;
};
check()?L=mid:R=mid;
}
printf("%.2Lf\n",L);
return 0;
}