万圣节的小L
题目描述:
今天是万圣节,小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
解题思路:
题目一看就是区间dp,但是数据范围n<=10000,普通的dp[i][j]肯定炸,所以这道题的难点就是状态转移,滚动数组正好能解决这个问题
定义一个数组dp[2][100010][2]:
第一维用来滚动,即轮换来记录上一次的状态和这一次的状态 now=last,last^=1
第二维记录位置
第三维记录方向(0左,1右)
依题意可得转移方程
dp[now][j][0]=min(dp[last][j+1][0]+x[j+1]-x[j],dp[last][j+1][1]+x[j+i]-x[j]);
dp[now][j][1]=min(dp[last][j][0]+x[j+i]-x[j],dp[last][j][1]+x[i+j]-x[i+j-1]);
1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 #define inf 0x7ffffff 5 using namespace std; 6 int n,now,last,x[10010],d[10010],dp[2][10010][2],ans; 7 int main() 8 { 9 scanf("%d",&n); 10 for(int i=1;i<=n;++i) 11 scanf("%d%d",&x[i],&d[i]); 12 for(int i=1;i<n;++i) 13 { 14 last=now; 15 now^=1; 16 for(int j=1;j<=n-i;++j) 17 { 18 dp[now][j][0]=min(dp[last][j+1][0]+x[j+1]-x[j],dp[last][j+1][1]+x[j+i]-x[j]); 19 dp[now][j][1]=min(dp[last][j][0]+x[j+i]-x[j],dp[last][j][1]+x[i+j]-x[i+j-1]); 20 if(dp[now][j][0]>=d[j]) 21 dp[now][j][0]=inf; 22 if(dp[now][j][1]>=d[i+j]) 23 dp[now][j][1]=inf; 24 } 25 } 26 ans=min(dp[now][1][0],dp[now][1][1]); 27 if(ans==inf) 28 printf("No solution"); 29 else 30 printf("%d",ans); 31 return 0; 32 }