轻功

Description

题目背景: 尊者神高达进入了基三的世界,作为一个 mmorpg 做任务是必不可少的,然而跑地图却令人十分不爽。好在基三可以使用轻功,但是尊者神高达有些手残,他决定用梅花桩练习轻功。 题目描述: 一共有 n 个木桩,要求从起点(0)开始,经过所有梅花桩,恰好到达终点 n,尊者神高达一共会 k 种门派的轻功,不同门派的轻功经过的梅花桩数不同,花费时间也不同。但是尊者神高达一次只能使用一种轻功,当他使用别的门派的轻功时,需要花费 W 秒切换(开始时可以是任意门派,不需要更换时间)。由于尊者神高达手残,所以经过某些梅花桩(包括起点和终点)时他不能使用一些门派的轻功。尊者神高达想知道他最快多久能到达终点如果无解则输出-1。

Input

第一行 n,k,W 接下来 k 行,每行为 ai 和 wi 代表第 i 种轻功花费 vi 秒经过 ai 个木桩。 接下来一行 Q 为限制条件数量。 接下来 Q 行,每行为 xi 和 ki 代表第 xi 个梅花桩不能使用第 ki 种门派的轻功经过。

Output

一行答案即所需最短时间。

Sample Input

Sample Input1:

6 2 5

1 1

3 10

2

1 1

2 1

Sample Input2:

6 2 5

1 1

3 10

0

Sample Output

Sample Output1:

18

样例解释 1: 先用第二种轻功花费 10 秒到 3,再用 5 秒切换到第一种轻功,最后再用 3 秒时间到 6.一共花费 10+5+3=18 秒

Sample Output2:

6

样例解释 2:

直接花费 6 秒到 6;

Data Constraint

20%的数据 n<=20,k<=10,Q<=200;

对于另外 20%的数据 W=0

对于另外 20%的数据 Q=0

所以数据满足 n<=500,k<=100,Q<=50000,vi<=1e7;

保证数据合法

Hint

Q:请问第一题可不可以往回跳

A:不可以

题解:

看到数据范围比较小,容易想到一个O(n*k*k)的二维线性DP。

先考虑如何O(1)判断能否使用一种轻功跳过一个区间。可以发现轻功并不多,因此可以对于每一个轻功维护一个前缀和,若两个端点相减为0,则代表这个区间中没有被禁止的木桩,否则就有。

然后考虑DP转移,设F(i,j)表示当前在i号木桩,用的是j种轻功跳过来的。

我们枚举j轻功,再枚举一个在j轻功之前的k轻功,则有两个转移:

j == k时

f[i][j] = min(f[i][j],f[i - j能经过的木桩数][k] + j的花费);

j !=k时

f[i][j] = min(f[i][j],f[i - j能经过的木桩数][k] + j的花费 + 轻功切换时间);

 最后注意一些边界处理,细节处理和-1的处理。我考试时把两个DP方程合在了一起,并且少了一两个条件,只拿了40分。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define MAXA 705
using namespace std;
typedef long long LL;
LL n,m,SwitchTime,Q,x,y;
LL Cost[MAXA],Pass[MAXA],f[503][103];
LL Pre[103][503],Ans = 999999999999;

int main() {
//	freopen("qinggong.in","r",stdin);
//	freopen("qinggong.out","w",stdout);
	scanf("%lld %lld %lld",&n,&m,&SwitchTime);
	for(int i=1;i<=m;i++)
	    scanf("%lld %lld",&Pass[i],&Cost[i]);
	scanf("%lld",&Q);
	for(int i=1;i<=Q;i++) {
		scanf("%lld %lld",&x,&y);
		Pre[y][x + 1] = 1;
	}
	for(int i=1;i<=m;i++)
	    for(int j=1;j<=n+1;j++)
	        Pre[i][j] += Pre[i][j-1];
	for(int i=0;i<=501;i++)
	    for(int j=0;j<=102;j++)
	        f[i][j] = 99999999999;
	for(int i=1;i<=m;i++)
	    f[1][i] = 0;
	for(int i=2;i<=n+1;i++)
	    for(int j=1;j<=m;j++)
	        for(int k=1;k<=m;k++) {
	        	if(i - Pass[j] >= 1 && j == k && !(Pre[j][i] - Pre[j][i - Pass[j] - 1]) && (f[i - Pass[j]][j] != 99999999999 || f[i - Pass[j]][k] != 99999999999))
	        	   f[i][j] = min(f[i][j],f[i - Pass[j]][k] + Cost[j]);
	        	else if(i - Pass[j] >= 1 && j != k && !(Pre[j][i] - Pre[j][i - Pass[j] - 1]) && (f[i - Pass[j]][j] != 99999999999 || f[i - Pass[j]][k] != 99999999999))
	        	   f[i][j] = min(f[i][j],f[i - Pass[j]][k] + Cost[j] + SwitchTime);
			}
//	for(int i=2;i<=n+1;i++) {
//		for(int j=1;j<=m;j++)
//		    printf("%d ",f[i][j]);
//		printf("\n");
//	}
	    
	for(int i=1;i<=m;i++)
	    Ans = min(Ans,f[n+1][i]);
	if(Ans == 99999999999)
	   printf("-1");
	else printf("%lld",Ans);
}

猜你喜欢

转载自blog.csdn.net/qq_41513352/article/details/83153357