简单题意:数轴上有 $n$ 个点,已知每个点的坐标 $a_i$ 和毁灭时间 $t_i$ 。开始时可以选择任意一点空降,每刻可以选择向左或者向右移动 $1$ 的单位长度,求出经过所有点的最短时间,如果不可能输出 No solution

首先因为经过点不会浪费时间,而且题目说 $a_i$ 是有序的,所以用 DP 记录经过区间 $[a_j,a_i]$,并且当前位置是 $x$ 的最小消耗时间。

考虑区间 DP。然而因为压根不知道 $a_i$ 的大小所以无法存下这个巨型状态,考虑优化。因为经过区间时保证每个点都被访问,贪心一下得出目前位置要么在区间最左端要么在最右段。那就以 $f_{j,i,0}$ 表示经过区间 $[j,i]$ 并且目前在点 $a_j$ 的最短时间,以 $f_{j,i,1}$ 表示经过区间 $[j,i]$ 并且目前在点 $i$ 的最短时间。

接下来考虑状态转移,也就是动规方程:

每个区间都是由 $2$ 个小区间合并起来的,以下用 $j$ 来表示区间左端,用 $i$ 来表示区间右端。

以 $f_{j,i,0}$ 举例子,每次要么从 $f_{j+1,i,0}$ 向左移动过 $1$ 个点,移动距离是 $a_{j+1}-a_j$ ;要么从 $f_{j+1,i,1}$ 走过 $i-j$ 个点,移动距离是 $a_i-a_j$。向右转移同理。还要在每次转移结束后还要判断,如果当前时间大于等于被刚刚经过的点的摧毁时间,标记为极大值。

接下来是初始状态,因为可以从任意一点空降落地瞬间经过任意单点,所以 $f_{i,i,0}=f_{i,i,1}=0$ 。最终答案是 $f_{1,n,0}$ 和 $f_{1,n,1}$ 的较小值。如果还是出现了极大值就表示无解。

两重循环,每次是 $10^4$ 总共是 $10^8$ 卡卡常数就完全不会超时。对代码有较大要求看不惯 $10^4$ 二维数组的可以写滚动数组,但是这题没必要。另外记得 UVA 是多组数据。

这里建议每个人按照自己区间 DP 中枚举左端和右端的方式写,因为方式实在是太太太多了,每次写不一样的就很容易出错。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using std::cin;using std::cout;
template<typename any>inline any max(any x,any y){return x>y?x:y;}
template<typename any>inline any min(any x,any y){return x<y?x:y;}
template<typename any>inline any abs(any x){return x>0?x:-x;}
struct treasure{int a,t;}a[10007];
int f[10007][10007][2];
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	std::ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int i,j,k;
	int n,m,T;

	while(cin>>n)
	{
		for(i=1;i<=n;i++)f[i][i][0]=f[i][i][1]=0;
		for(i=1;i<=n;i++)cin>>a[i].a>>a[i].t;
		for(i=2;i<=n;i++)for(j=i-1;j;j--)
		{
			f[j][i][0]=min(f[j+1][i][0]+a[j+1].a-a[j].a,f[j+1][i][1]+a[i].a-a[j].a);
			if(f[j][i][0]>=a[j].t)f[j][i][0]=0x3f3f3f3f;
			f[j][i][1]=min(f[j][i-1][1]+a[i].a-a[i-1].a,f[j][i-1][0]+a[i].a-a[j].a);
			if(f[j][i][1]>=a[i].t)f[j][i][1]=0x3f3f3f3f;
		}
		int ans=min(f[1][n][0],f[1][n][1]);
		if(ans==0x3f3f3f3f)cout<<"No solution\n";
		else cout<<ans<<'\n';
	}

	return 0;
}