【NOIP2013模拟11.7A组】图书馆

【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,j1,kval+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]kk/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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值