原题
LRJ入门经典-0907万圣节的小L306 |
难度级别:B; 运行时间限制:1000ms; 运行空间限制:256000KB; 代码长度限制:2000000B |
试题描述
|
今天是万圣节,小L同学开始了一年一度的讨要糖果游戏,但是在刚刚过去的比赛中小有成就的他打算给自己增加一点难度:如果没有讨到每一家的糖果就算输。 已知小L共有n(n不大于10000)个邻居,他们都在同一条街上(可以近似看成一条直线),第i个邻居的坐标是xi。L同学的妈妈会在一开始把他送到任意邻居的门前。现在已知所有邻居会在di时间后休息(休息以后不能再去打扰),求访问完所有点的最短时间,如果无解输出“No solution”。 |
输入
|
输入第一行为一个正整数表示n,接下来n行,每行两个用空格隔开的数,分别表示第i个邻居的位置和休息时间。
|
输出
|
输出一个数,表示最短时间,无解输出“No solution”。
|
输入示例
|
5
1 3 3 1 5 8 8 19 10 15 |
输出示例
|
11
|
分析1
当一看到“(n不大于10000)”时,就大约知道这是一道动态规划了。
再一看到“(可以近似看成一条直线)”时,就大概知道这是一道区间DP。
一切的一切都在说明一件事:读题很重要(语文很重要)。
状态:
dp[i][j][0]:将第i~j户邻居的糖果在他们休息之前全部要到,并在最后停在左边的最短时间。
dp[i][j][1]:将第i~j户邻居的糖果在他们休息之前全部要到,并在最后停在右边的最短时间。
转移方程:
这个可能有点难:
x=dp[i][j-1][1]+a[j]-a[j-1];
y=dp[i][j-1][0]+a[j]-a[i];
if(x>b[j]) x=INF;
if(y>b[j]) y=INF;
dp[i][j][1]=min(dp[i][j][1],min(x,y));
x=dp[i][j-1][0]+a[j]-a[j-1];
y=dp[i][j-1][1]+a[j]-a[i];
if(x>b[j]) x=INF;
if(y>b[j]) y=INF;
dp[i][j][0]=min(dp[i][j][0],min(x,y));
答案:
很显然是min(dp[1][n][0],dp[1][n][1])。
代码1
先上一段错误的代码:
分析2
显然上段代码的dp数组开不下了!
不要慌!
冷静思考!
首先想解决办法:
1、(谁都能想到)压缩数组
2、(这个有点儿难)用滚动数组
将dp[i][j][k]的i压缩成2
直接上代码!
代码2
#include<iostream> #include<cstring> #include<algorithm> #define INF 1073741824 using namespace std; int dp[2][10001][2],n; int a[10001],b[10001]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]>>b[i]; for(int i=1;i<=n;i++) dp[0][i][0]=dp[1][i][1]=0; for(int k=1;k<n;k++) { for(int i=1;i+k<=n;i++) { int j=i+k,x,y; int I=(i%2)^1; dp[i%2][j][0]=dp[i%2][j][1]=INF; x=dp[i%2][j-1][1]+a[j]-a[j-1];y=dp[i%2][j-1][0]+a[j]-a[i]; if(x>b[j]) x=INF; if(y>b[j]) y=INF; dp[i%2][j][1]=min(dp[i%2][j][1],min(x,y)); //cout<<"i:"<<i<<" j:"<<j<<" o:1"<<" x:"<<x<<" y:"<<y<<" dp:"<<dp[i%2][j][1]<<endl; x=dp[I][j][0]+a[i+1]-a[i];y=dp[I][j][1]+a[j]-a[i]; if(x>b[i]) x=INF; if(y>b[i]) y=INF; dp[i%2][j][0]=min(dp[i%2][j][0],min(x,y)); //cout<<"i:"<<i<<" j:"<<j<<" o:0"<<" x:"<<x<<" y:"<<y<<" dp:"<<dp[i%2][j][0]<<endl<<endl; } } if(min(dp[1][n][0],dp[1][n][1])==INF) cout<<"No solution"; else cout<<min(dp[1][n][0],dp[1][n][1]); return 0; }