1. 程式人生 > >jzoj 5907.【NOIP2018模擬10.16】輕功

jzoj 5907.【NOIP2018模擬10.16】輕功

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;

分析: 很顯然的dp啦。 設f[i][j]f[i][j]為到ii這根木樁,當前氣功為jj的最小代價。 顯然如果中間都沒有限制的話,那麼有 f[i][j]=min(f[i][j],f[ilen[j]][j]+t[j])f[i][j]=min(f[i][j],f[i-len[j]][j]+t[j])

然後找到ii中時間花費最小的一個型別,看看切成其他型別是否更優。 判斷中間有沒有限制可以跑字首和。 複雜度是O(nk)O(nk)的。

程式碼:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const int maxn=507;
const LL inf=1e17;

using namespace std;

int n,m,q,x,y,sum[maxn][maxn],len[maxn],t[maxn];
LL w,f[maxn][maxn];

bool check(int l,int r,int k)
{
	LL num=sum[r][k];
	if (l) num-=sum[l-1][k];
	return (num==0);
}

int main()
{
	freopen("qinggong.in","r",stdin);
	freopen("qinggong.out","w",stdout);
	scanf("%d%d%lld",&n,&m,&w);
	for (int i=1;i<=m;i++) scanf("%d%d",&len[i],&t[i]);
	scanf("%d",&q);
	for (int i=1;i<=q;i++)
	{
		scanf("%d%d",&x,&y);
		sum[x][y]++;
	}
	for (int j=1;j<=m;j++)
	{
		for (int i=1;i<=n;i++) sum[i][j]+=sum[i-1][j];
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++) f[i][j]=inf;
	}	
	LL minn;
	for (int i=1;i<=n;i++)
	{
		minn=inf;
		for (int j=1;j<=m;j++)
		{
			if (i-len[j]>=0)
			{
				if (check(i-len[j],i,j))
				{
					f[i][j]=min(f[i][j],f[i-len[j]][j]+t[j]);
				}
			}
			minn=min(f[i][j],minn);
		}
		for (int j=1;j<=m;j++) f[i][j]=min(f[i][j],minn+w);
	}
	if (minn==inf) printf("-1");
	          else printf("%lld",minn);
}