天堂 (heaven)
Problem: heaven.pas/c/cpp
Input: heaven.in
Output: heaven.out
Memory Limit: 256 MB
Time Limit: 2 sec
题目描述
每一个要上天堂的人都要经历一番考验,当然包括小X,小X开始了他进入天堂的奇异之旅。地狱有18层,天堂竟然和地狱一样,也有很多很多层,天堂共有N层。从下到上依次是第1,2,3,…,N层,天堂的每一层都是一个延伸无限远的地板,在地板上人可以任意走动,层与层之间是平行关系,每一层的地板都是由人不能穿过的物质构成,幸好每一层地板上有且仅有1个人可以通过的洞口。
我们可以把小X和洞口,还有下面提到的气球店都看成点,坐标是二维的。小X开始在第1层的(0,0).
小X的重量为M,第i层与第i+1层之间的特殊气体能浮起的重量为Wi ,每一层的地面上散落了若干个气球店,多个气球店可以在同一点,每个气球可以浮起的重量是1,去一个气球店一次只能领取一个气球,不能连续在一个气球店领取气球,当然你可以在两个气球店之间来回跑,每个气球店供应的气球都是无限多的。第i层的气球只能在第i层进入第i+1层时使用,当小X在第i层,只有站到了第i+1层洞口的位置(在其它位置不会浮起),并且自身的重量小于等于气球和特殊气体浮起重量的总和,才可以进入第i+1层。小X想知道他要到达第N层走过的长度最少是多少?题目保证有解。
输入文件
第1行: 三个正整数N,M,Q(Q表示气球店)
第2行: 共2*(N-1)个整数,每两个数描述1个洞口坐标,第i对xi,yi表示第i+1层的洞口位置(xi,yi)。
第3行: 共N-1个整数,第i个数为Wi。
往后Q行,每行三个整数x,y,z , 表示第Z层有一个气球店,坐标为(x,y)
输出文件
1个实数L,保留两位小数,表示小X最少要走的长度。
样例输入
3 10 4
0 0 1 2
9 0
0 1 1
2 3 1
0 1 2
1 1 2
样例输出
13.00
注释
【样例解释】
在第一层从(0,0)出发到(0,1)取得1个气球并返回(0,0)即可到达第二层。长度:2.00
在第二层,从(0,0)到(0,1)领取气球,再到(1,1)领取气球,两个点来回跑,第5次到达(1,1)时恰好气球数达到10,走到(1,2)即可到达第3层终点。长度:11.00
总长度:13.00
【数据范围】
2<=N<=100
每层的气球店数目不超过50。
0<=M<=100, 0<=Wi<=100
坐标-3000<=x,y<=3000
这一题初看还以为是搜索,结果写出来超时完了,0分。。。。后来看题解才知道可以用动规解决
先不考虑有那么多层,假设只有一层,那么我们可以用f[i][j]表示已经得到了 i 个气球,当前站在第 j 个气球店的位置,那么可以维护所有的f[i][j]
现在我们加上层数,我们只需要分别计算出每一层最小的,然后累加即可
需要注意的是,计算完了每一层要加上一个从最后所在气球店到当前层到下一层的距离
具体看看代码,有很详细的注释
C++ Code
/*
C++ Code
http://blog.csdn.net/jiangzh7
By jiangzh
*/
#include<cstdio>
#include<cmath>
using namespace std;
#define MAXN 110
#define min(a,b) ((a)<(b)?(a):(b))
struct point{int x,y;};
int n,m,q,w[MAXN];
point tar[MAXN],qiu[MAXN][60];//tar记录每一层洞口位置 qiu记录每一层气球位置
int num[MAXN];//记录每一层的气球数
double f[MAXN][60];//f[i][j]表示已获得 i 个气球,站在当前层第 j 个气球店位置的最优距离
double total;
double dis(int x1,int y1,int x2,int y2)
{ return sqrt((double)(x1-x2)*(x1-x2)+(double)(y1-y2)*(y1-y2)); }
double calc(int k)
{
for(int i=0;i<=110;i++)
for(int j=0;j<=60;j++)
f[i][j]=1e8; //赋初值
point now;
if(k==1) {now.x=0;now.y=0;}
else now=tar[k-1]; //处理处现在所在位置
int need=m-w[k]; //当前这一层需要的气球数
if(need<=0) return dis(now.x,now.y,tar[k].x,tar[k].y);//如果不需要气球 就直接去洞口进入下一层
for(int i=1;i<=num[k];i++)
f[1][i]=dis(now.x,now.y,qiu[k][i].x,qiu[k][i].y);//处理处当前层获得1个气球所需走的距离
double ans=1e8;
if(need==1)
{
for(int i=1;i<=num[k];i++) ans=min(ans,f[1][i]+dis(qiu[k][i].x,qiu[k][i].y,tar[k].x,tar[k].y));
return ans; //处理出只需一个气球的距离
}
for(int i=2;i<=need;i++) //递推出需要走的最短距离
for(int j=1;j<=num[k];j++)
for(int t=1;t<=num[k];t++)
if(j!=t)
f[i][j]=min(f[i][j],f[i-1][t]+dis(qiu[k][j].x,qiu[k][j].y,qiu[k][t].x,qiu[k][t].y));
ans=1e8;
for(int i=1;i<=num[k];i++)
ans=min(ans,f[need][i]+dis(qiu[k][i].x,qiu[k][i].y,tar[k].x,tar[k].y));//找出所有方案中最优的
return ans;
}
int main()
{
freopen("heaven.in","r",stdin);
freopen("heaven.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<n;i++) scanf("%d%d",&tar[i].x,&tar[i].y);//读入每一层的通向上面一层的入口位置
for(int i=1;i<n;i++) scanf("%d",&w[i]);//读入每一层能托起的重量
int x,y,z;
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&x,&y,&z);
qiu[z][++num[z]].x=x;qiu[z][num[z]].y=y; //读入每一层气球店的坐标 num[z]表示第 z 层的气球店个数
}
total=0.0;
for(int i=1;i<n;i++)//分别计算出每一层的最优值 累加
total+=calc(i);
printf("%.2lf",total);
return 0;
}