SNOI省选模拟赛Round3 T1 路径规划path DP

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值