[BZOJ4244]邮戳拉力赛

Description

IOI铁路是由N+2个站点构成的直线线路。这条线路的车站从某一端的车站开始顺次标号为0...N+1。
这条路线上行驶的电车分为上行电车和下行电车两种,上行电车沿编号增大方向行驶,下行电车沿编号减小方向行驶。乘坐这两种电车的话,移动1站的距离需要T秒。换句话说,乘坐上行电车从车站i走到车站i+1需要T秒,称作下行电车从车站i走到车站i-1也需要T秒。你不能在0号车站乘坐下行电车,或在N+1号车站乘坐上行电车。由于电车发车的频率非常高,你可以无视等待电车消耗的时间。
每个车站设有上行电车的站台和下行电车的站台,连接两个站台的道路上设有邮戳台。
现在,IOI铁路召开了邮戳拉力赛。在拉力赛中,选手需要从0号车站的上行电车站台出发,在1...N号车站各盖一枚邮戳,最终到达N+1号车站的上行电车站台即可完成。
为了在每个车站盖上邮戳,必须从电车上下来,步行走到车站通路上的邮戳台。在i号车站的上行电车站台、邮戳台、下行电车站台之间移动所消耗的时间如下所示:
从车站i的上行电车站台到邮戳台的时间为Ui秒
从车站i的邮戳台到上行电车站台的时间为Vi秒
从车站i的下行电车站台到邮戳台的时间为Di秒
从车站i的邮戳台到下行电车站台的时间为Ei秒
邮戳拉力赛的选手只能访问0号车站与N+1号车站各一次。1...N号车站都可以访问任意多次。
 
现在给出有邮戳台的车站个数、乘坐电车移动一站的时间、在每个车站的上行电车站台、邮戳台、下行电车站台之间移动所消耗的时间,请你求出完成邮戳拉力赛的最短时间。
这个时间包括从0号车站出发,按下N个邮戳后到达N+1号车站的时间。无视等车的时间和按邮戳的时间。

Input

第一行两个空格分隔的整数N和T,表示有N+2个车站,电车行驶一站的距离需要T秒
接下来N行,第i行有四个空格分隔的整数Ui,Vi,Di,Ei,分别表示:
从车站i的上行电车站台到邮戳台的时间为Ui秒
从车站i的邮戳台到上行电车站台的时间为Vi秒
从车站i的下行电车站台到邮戳台的时间为Di秒
从车站i的邮戳台到下行电车站台的时间为Ei秒

Output

输出一行一个整数,表示完成邮戳拉力赛的最短时间。

Sample Input

4 1
1 1 1 1
1 9 9 1
9 9 1 1
1 9 9 1

Sample Output

23

HINT

从车站0出发,按照2-1-4-3-1-5的顺序访问车站可以达到最短时间。
1<=N<=3000
1<=T<=10^5
1<=Ui<=10^5(1<=i<=N)
1<=Vi<=10^5(1<=i<=N)
1<=Di<=10^5(1<=i<=N)
1<=Ei<=10^5(1<=i<=N)
 
好几天没更博客了,最近做了一道很妙的背包题,记录一下
首先我们很容易发现,最后的路线一定是一条上行线走到头外加几个环
然后我们为了盖邮戳,一共有四种搞法:

而且因为除了一条直线之外的全是环,所以如果存在$x$种第一种取法,就必定有$x$种第二种取法与之对应

而对于第三种和第四种取法,由于不会改变行走的方向,所以不用单独处理

这就可以对应到括号序列上来,第二种取法是左括号,第一种取法是右括号,这样对应的原因是你可以用二在后一在前来消除影响,但如果一在前就无法消除影响了,具体原因就是线路方向

然后还有需要注意的是,如果我们要用第三种取法,就要保证他之前有第二种取法的影响还未被消掉(你得保证此时的可以到达下行线)

所以我们设$f_{ij}$表示到$i$还剩$j$个左括号的时候最小代价是多少

按照上面四种情况转移即可

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int n,T;
 6 int f[3001][3001];
 7 int main() {
 8     memset(f,63,sizeof(f)),f[0][0]=0;
 9     scanf("%d%d",&n,&T);
10     for(int i=1;i<=n;i++) {
11         int u,v,d,e;scanf("%d%d%d%d",&u,&v,&d,&e); 
12         for(int j=1;j<=n;j++) f[i-1][j]+=j*T*2;
13         //下->中->上 
14         for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i-1][j-1]+d+v);
15         //上->中->下 
16         for(int j=0;j<n;j++) f[i][j]=min(f[i][j],f[i-1][j+1]+u+e);
17         //上->中->上
18         for(int j=0;j<=n;j++) f[i][j]=min(f[i][j],f[i-1][j]+u+v);
19         //下->中->下
20         for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i-1][j]+d+e);
21         //消除上->中->下的影响 
22         for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i][j-1]+d+v);
23         //消除下->中->上的影响 
24         for(int j=n-1;j;j--) f[i][j]=min(f[i][j],f[i][j+1]+u+e); 
25     }
26     printf("%d\n",f[n][0]+(n+1)*T);
27     return 0;
28 }

 

posted @ 2018-12-09 14:27  Slr  阅读(244)  评论(0编辑  收藏  举报