usaco 地震 && 奶牛观光

Usaco 地震:

Description

一场地震把约翰家的牧场摧毁了,坚强的约翰决心重建家园。约翰已经重建了N个牧场, 现在他希望能修建一些道路把它们连接起来。研究地形之后,约翰发现可供修建的道路有M 条。碰巧的是,奶牛们最近也成立一个工程队,专门从事修复道路。而然,奶牛们很有经济 头脑,如果无利可图,它们是不会干的。

奶牛们关注的是挣钱速度,即总利润和总施工时间的比值。约翰和奶牛达成了协议,奶 牛负责修建道路,将所有牧场连通,而约翰需要支付F元。每条道路都有自己的施工时间和 建造成本。连接两个相同的牧场的道路可能有多条。保证所有的牧场必定是可连通的,不过 也有可能一些道路的建造成本之和会超过F。

请帮助奶牛们选择修复哪些道路,才能使单位时间的利润最大?

Input Format

第一行:三个整数: N,M和F,1≤N≤400,1≤M≤10000,1≤F≤2×10^9

第二行到M+1行:第i +1行表示第i条道路的信息,有四个整数:Ui,Vi,Ci和Ti,Ui和Vi表示这条道路连接的牧场编号,Ci表示这条路的施工时间,Ti表示建造成本,1≤Ui≤N,1≤Vi≤N,1≤Ci≤2×10^9,1≤Ti≤2×10^9

Output Format

第一行:一个保留四位小数的浮点数,表示奶牛们能挣到的最大单位时间利润,如果奶 牛们无钱可赚,则输出0.0000

-------------------------------------------------------------------------------------

显然这题是求最优比率生成树,

对最优比率 k 而言 (∑t)*k=F-∑v

F=(∑t)*k+∑v=∑(t*k+v)

显然上面这个式子具有单调性,随 k 而单调递增

因此可以对 k 进行二分,

以 ( k*t +v )  作为关键字做生成树

若∑(t*k+v)>F 下调 k 否则上调 k

Ps.注意精度问题,图图大神说 :二分的精度至少要比保留的小数位数多2位。例如这题要求保留到0.0001,那么二分的精度应该为0.000001

代码如下:

usaco 地震 && 奶牛观光usaco 地震 && 奶牛观光
 #include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<iostream>
#define O 0.00000001
#define LL long long
#define INF 2000000000
using namespace std;
struct Point{
int x,y;
double c,t;
}a[];
int f[],n,m;
double l,r,ans,F,mid;
bool cmp(const Point&X,const Point&Y){
return X.c+mid*X.t<Y.c+mid*Y.t;
}
int find(int u){
return f[u]==u ? f[u] : f[u]=find(f[u]);
}
bool check(long double lim){
sort(a+,a++m,cmp);
for(int i=;i<=n;i++) f[i]=i;
int tot=;
double temp=;
for(int i=;i<=m;i++)
if(find(a[i].x)!=find(a[i].y)){
f[find(a[i].x)]=f[find(a[i].y)];
temp+=a[i].c+mid*a[i].t;
++tot;
if(temp>F) return false ;
}
if(tot<n-) {
printf("0.000");
exit();
}
return true;
}
int main(){
scanf("%d%d%lf",&n,&m,&F);
for(int i=;i<=m;i++)
scanf("%d%d%lf%lf",&a[i].x,&a[i].y,&a[i].c,&a[i].t);
l=,r=F;
while(r-l>=O){
mid=(l+r)/;
if(check(mid)){
ans=mid;
l=mid+O;
} else r=mid-O;
}
printf("%.4lf",ans);
}

-------------------------------------------------------------------------------------

Usaco 奶牛观光:

Description

作为对奶牛辛勤工作的回报,约翰决定带她们去附近的大城市玩一天。这个城市有L个景点,参观第i个景点会给奶牛带来Fi点欢乐度。第二天一早,奶牛可以*选择从一个景点出发,约翰会负责开车把她们送到那里,但她们晚上必须回到这个景点和约翰汇合。

大城市里都是单行道,第i条道路从第L1i个建筑通向第L2i个建筑,走完需要Ti的时间。奶牛讨厌走路,定义一条游览线路的“欢乐指数”为参观这条线路上所有景点的欢乐度之和与花在路上的时间之和的比值。欢乐指数越大的线路越受欢迎。当然,参观同一景点两次不会带来双倍的欢乐。假设奶牛们至少参观两个景点,请帮她们找到一条欢乐指数最大的线路。

Input Format

第一行:两个用空格分开的整数:L和P,1 ≤ L ≤ 1000,2 ≤ P ≤ 5000

第二行到L + 1行:第i + 1行有一个整数Fi,1 ≤ Fi ≤ 1000

第L + 2行到L + P + 1行:第i + L + 1行有三个用空格分开的整数:L1i,L2j和Ti,1 ≤ L1i ≤ L,1 ≤ L2i ≤ L,1 ≤ Ti ≤ 1000

Output Format

第一行:输出一个实数,表示最大的欢乐指数,保留两位小数

-----------------------------------------------------------------

好把,这题是求最优比率最短路(环)( Usaco真是一站式服务- =)

最优比率 (∑t)*k=∑f => ∑(t*k-f)=0

和上一题一样,k 具有单调性,也显然可以二分

那如何判断这个环是否小于 0(负权环)

利用SPFA:

图图大神的方法是:

用edge[i]表示到i的最短路经过的边数,若边数 = N,则存在负权环

我用了种比较傻的:

time[i]表示该点被更新了几次,如果大于 n 显然存在负权环

图图大神还说:SPFA的使用十分灵活,经常配合动规使用,所以往往队列里的初始状态是不唯一的。

代码如下:

usaco 地震 && 奶牛观光usaco 地震 && 奶牛观光
 #include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<iostream>
#include<queue>
#define INF 999999999
#define N 5011
const double E=1e- ;
using namespace std;
queue <int> q;
int n,P;
int T,last[N],next[N],s[N];
double sum[N],t[N],l,r,mid,ans;
bool check(){
int time[N]; memset(time,,sizeof(time));
double d[N]; for(int i=;i<=n;i++) d[i]=INF;
bool in[N]; memset(in,,sizeof(in));
time[]=in[]=; d[]=;
while(!q.empty()) q.pop();
for(q.push();!q.empty();q.pop()){
int now=q.front();
for(int i=last[now];i;i=next[i]){
double w=t[i]*mid-sum[now];
if(d[s[i]]+E>d[now]+w){
d[s[i]]=d[now]+w;
if(!in[s[i]]){
in[s[i]]=;
q.push(s[i]);
if(++time[s[i]]>n) return ;
}
}
}
in[now]=;
}
return ;
}
int main(){
scanf("%d%d",&n,&P);
for(int i=;i<=n;i++) scanf("%lf",&sum[i]);
for(int i=;i<=P;i++){
int x,y;
double v;
scanf("%d%d%lf",&x,&y,&v);
next[++T]=last[x]; last[x]=T; s[T]=y; t[T]=v;
}
l=E,r=;
while(r-l>=E){
mid=(l+r)/;
if(check()){
ans=mid;
l=mid+E;
} else r=mid-E;
}
printf("%.2lf",ans);
}
上一篇:写自己的一个pdo数据库操作框架


下一篇:用tp框架来对数据库进行增删改