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,但方程一直想着前i个田放了j个门的最短时间,发现无法转移,GG。
【正文】
这真的是一道十分好的dp题啊,思维jiang化的人就会像我一样想到上面的方程然后偷税的打出GG。
所以要换个角度来考虑问题。
首先可以得出一个显然但重要的结论:传送门放在节点上只会让答案更优,所以所有门一律视作放在节点上。
假设现在只有两个点l,r有传送门,那么对于一条路径(a,b),a<b,如果a,b都在这两个点外面,那么对着两个点之间的代价没有影响,忽略。
如果a在l,r之间,b在l,r外,那么从a到b有两种方法:一种是直接去,一种是回头去l传送到r再去。a在外,b在内与a,b都在同理。
那么我们可以先预处理出一个数组cost[l][r],表示在只有l,r有传送门的情况下,经过l,r这段区间的代价。
于是乎就可以转移了。
定义dp[i][j]表示在第i个点放传送门,用了j个门的最小代价。
转移为dp[i][j]=min(dp[k][j-1]+cost[k][i])。
代码:
#include<bits/stdc++.h>
#define inf 0x1f1f1f1f1f1f1f1fll
using namespace std;
typedef long long LL;
LL read()
{
char c;LL sum=0,f=1;c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,m;
LL x[1005],a[1005];
LL dp[1005][1005],cost[1005][1005];
bool bh(LL pos,LL l,LL r)
{
return pos>=l && pos<=r;
}
int main()
{
n=read();
a[0]=-inf;a[n+1]=inf;
for(int i=1;i<=n;i++)
x[i]=read(),a[i]=x[i];
m=read();
n+=2,m+=1;
sort(a,a+n);
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
for(int k=1;k<(n-2);k++)
{
LL l=min(x[k],x[k+1]),r=max(x[k],x[k+1]);
if(bh(l,a[i],a[j]) && bh(r,a[i],a[j]))
cost[i][j]+=min(r-l,l-a[i]+a[j]-r);
else if(bh(l,a[i],a[j])) cost[i][j]+=min(a[j]-l,l-a[i]);
else if(bh(r,a[i],a[j])) cost[i][j]+=min(a[j]-r,r-a[i]);
}
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++)
for(int k=i-1;k>=0;k--)
dp[i][j]=min(dp[i][j],dp[k][j-1]+cost[k][i]);
printf("%lld\n",dp[n-1][m]);
return 0;
}