A 路径规划(path.pas/c/cpp)
TL:1S ML:128MB
【Description】
kAc在数轴上有N片西瓜地。第 i片的坐标是X[i](注意 X并没有排序)。任意两片西瓜地坐标不同。有一天他要给这N片西瓜地浇水。初始他在X[1]的位置。他必须按1..N 的顺序浇水,也就是说,必须先去X[1],再去X[2]...最后到X[n](他可以沿着坐标轴正方向或者负方向走)。
给西瓜地浇水不需要花费时间。每走1单位的距离需要花费1 的时间。 现在kAc为了节约时间 打算建立K个超时空传送站。如果两个位置P、Q,在这两处位置都有超时空传送站,那么我们可以瞬间转移过去(从 P到 Q或者从Q到 P) 。
现在他想知道 如果要给这 N片西瓜地都浇好水,最短需要多少时间?
【Input】
第一行一个整数N
接下来N行,每行一个整数X[i]
最后一行,一个整数K。
【Output】
输出最短时间。
【Sample Input】
4
0 6 8 2
2
【Sample Output】
6
【Hint】
样例解释:
在1、7 两个位置建立传送站。
30%:N<=5 坐标范围<=100
100%:N<=50 坐标范围<=10^9
想法:考场上一眼看出dp,列出了正确的状态,但是因为不知道怎样转移而GG,之后才知道这是一个dp套dp。。。
解法:cost[i][j]表示i和j建传送门,其他地方都不建,所有路径的花费;
dp[i][j]表示前i个点j个传送门时的最小花费。
转移:dp[i][j]=min(dp[i][j],dp[k][j-1]+cost[k][i]);
cost区间分类讨论即可。
代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
ll n,K,tmp,dp[1005][1005],cost[1005][1005],inf=0x1f1f1f1f1f1f1f1fll,ans=inf,dis[55],x[55];
int main()
{
memset(dp,inf,sizeof(dp));
dp[0][0]=0;
scanf("%lld",&n);
for(int i=0;i<n;i++) scanf("%lld",&tmp),x[i]=dis[i]=tmp;
scanf("%lld",&K);
dis[n]=inf;
dis[n+1]=-inf;
sort(dis,dis+n+2);
for(int i=0;i<=n+1;i++)
for(int j=i+1;j<=n+1;j++)
for(int k=0;k<n-1;k++)
{
ll l=min(x[k],x[k+1]),r=max(x[k],x[k+1]);
if(l>=dis[i]&&l<=dis[j]&&r>=dis[i]&&r<=dis[j]) cost[i][j]+=min(r-l,l-dis[i]+dis[j]-r);
else if(l>=dis[i]&&l<=dis[j]) cost[i][j]+=min(dis[j]-l,l-dis[i]);
else if(r>=dis[i]&&r<=dis[j]) cost[i][j]+=min(dis[j]-r,r-dis[i]);
}
for(int i=1;i<=n+1;i++)
for(int j=1;j<=K+1;j++)
for(int k=i-1;k>=0;k--)
dp[i][j]=min(dp[i][j],dp[k][j-1]+cost[k][i]);
for(int i=0;i<=K+1;i++) ans=min(ans,dp[n+1][i]);
printf("%lld\n",ans);
}