【NOIP2013模拟11.7A组】图书馆
题目
圣玛格丽特大图书馆是一座由石材砌成的角柱型高塔,是欧洲屈指可数的巨大书库。图书馆整面墙壁都是巨大的书架,书架与书架之间就像巨大的迷宫一般,以细窄的木制楼梯连结。大图书馆的最高处是一个绿意盎然的植物园,维多利加正在那无聊地看着书。今天,一如往常地,久城要爬上这迷宫般的楼梯给维多利加送讲义。
图书馆墙壁上有N个平台,编号为1到N,入口为1号,植物园为N号。有M个连接两个不同平台的楼梯,爬每个楼梯需要消耗一定的体力值。楼梯一定是由低处通往高处的,为了省时间,久城只能选择上楼梯而不能下楼梯,也就是说,楼梯之间不会形成环路。而且,出于人性化考虑,不管久城选择哪条路线上楼,他爬的楼梯数量一定小于20。
为了使体力消耗尽量平稳,久城需要选择一条“每个楼梯消耗体力值的方差最小”的路径上楼。请帮助久城计算出这个最小方差。
Input
第一行包含2个整数N,M,表示图书馆的平台数和楼梯数;
接下来M行,每行3个数x,y,z,表示存在一条由平台x通往平台y的楼梯,爬这个楼梯需要消耗z的体力值。
Output
一行1个实数,表示最小方差,精确到小数点后4位。
Sample Input
4 4
1 2 1
2 4 3
1 3 2
3 4 3
Sample Output
0.2500
Data Constraint
对于30%的数据,N≤10,M≤20;
另有20%的数据,N≤35,M≤220,z∈{0,1};
对于100%的数据,2≤N≤50,M≤300,0≤z≤50,保证至少存在一条由1到N的路径。
解题思路
良心出题人。
首先,我们知道这是一个有向无环图,且起点到终点的路径数小于20,那么,我们可以用DP解决。
方差公式的转移就不提了……
设
F
i
,
j
,
k
F_{i,j,k}
Fi,j,k表示现在在第
i
i
i个楼梯,一共走过
j
j
j个楼梯,消耗了
k
k
k的体力值的楼梯长度平方和。
就有状态转移方程:
F
i
,
j
,
k
=
M
i
n
(
F
i
,
j
,
k
,
F
f
r
o
m
,
j
−
1
,
k
−
v
a
l
+
v
a
l
2
)
F_{i,j,k}=Min(F_{i,j,k},F_{from,j-1,k-val}+val^2)
Fi,j,k=Min(Fi,j,k,Ffrom,j−1,k−val+val2)。
f
r
o
m
from
from表示当前楼梯的起点,
v
a
l
val
val表示其长度。
最后就是统计答案:
M
i
n
(
f
[
n
]
[
j
]
[
k
]
−
k
∗
k
/
j
)
/
j
Min(f[n][j][k]-k*k/j)/j
Min(f[n][j][k]−k∗k/j)/j。
Code
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
struct way
{
int last,to,val;
}w[305];
int f[55][25][1005];
int n,m,cnt,head[55];
double ans,an;
inline void add(int from,int to,int val)
{
w[++cnt].last=head[from];
w[cnt].to=to;
w[cnt].val=val;
head[from]=cnt;
}
inline int read()
{
int an=0,ww=1;
char ch=getchar();
while ((ch<'0')||(ch>'9'))
{
if (ch=='-')ww=-1;
ch=getchar();
}
while ((ch>='0')&&(ch<='9'))
{
an=(an<<1)+(an<<3)+(ch^48);
ch=getchar();
}
return an*ww;
}
int main()
{
freopen("library.in","r",stdin);
freopen("library.out","w",stdout);
int x,y,z;
n=read();m=read();
for (register int i=1;i<=m;i++)
{
x=read();y=read();z=read();
add(y,x,z);
}
for (register int i=1;i<=n;i++)
for (register int j=0;j<20;j++)
for (register int k=0;k<=1000;k++)
f[i][j][k]=1e5;
f[1][0][0]=0;
for (register int j=1;j<20;j++)
{
for (register int i=2;i<=n;i++)
{
for (register int k=0;k<=1000;k++)
{
for (register int l=head[i];l;l=w[l].last)
{
x=w[l].to;z=w[l].val;
if (k<z)continue;
if (f[x][j-1][k-z]<1e5)
f[i][j][k]=min(f[i][j][k],f[x][j-1][k-z]+z*z);
}
}
}
}
ans=1000000.0;
for (register int j=1;j<20;j++)
{
for (register int k=0;k<=1000;k++)
{
if (f[n][j][k]==1e5)continue;
an=double(1.0*f[n][j][k]-1.0*k*k/j)*1.0/j;
if (abs(an)<ans)ans=abs(an);
}
}
printf("%.4lf",ans);
fclose(stdin);
fclose(stdout);
return 0;
}