NOIP模拟 轻功

  • 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[当前位置][跳到这个位置的功法]为最低代价
我们用一个last数组保存上一次禁止这种功法的位置(这里用链式前向星讲位置向禁止的功法连一条边)
然后可以通过last和当前位置判断出当前位置是否可以用某种功法跳过来
下面给出代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 1e15;
ll n,k,w,q;
struct xx
{
	ll waste;
	ll can;
}a[105];
struct xxx
{
	ll next;
	ll to;
}ban[50005];
ll f[505][105];
ll cnt,head[505],last[105];

void add(int u,int v)
{
	ban[++cnt].next = head[u];
	ban[cnt].to = v;
	head[u] = cnt;
	return;
}
void in(ll &x)
{
	x = 0;
	ll f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	x *= f;
	return;
}
void out(ll x)
{
	if (x < 0)
	{
		putchar('-');
		x = -x;
	}
	if (x > 9)
	{
		out(x / 10);
	}
	putchar(x % 10 + '0');
}
int main()
{
//	freopen("qinggong.in","r",stdin);
//	freopen("qinggong.out","w",stdout);
	for (int i = 0; i <= 502; i++)
	{
		for (int j = 0; j <= 102; j++)
		{
			f[i][j] = INF;
		}
	}
	in(n);
	in(k);
	in(w);
	for (int i = 1; i <= k; i++)
	{
		in(a[i].can);
		in(a[i].waste);
	}
	in(q);
	for (int i = 1; i <= q; i++)
	{
		ll pos,cant;
		in(pos);
		in(cant);
		add(pos,cant);
	}
	for (int i = 1; i <= k; i++)
	{
		f[0][i] = 0;
	}
	for (int pos = 1; pos <= n; pos++)
	{
		for (int i = head[pos]; i; i = ban[i].next)
		{
			last[ban[i].to] = pos;
		}
		for (int type = 1; type <= k; type++)
		{
			if (last[type])
			{
				if (pos - last[type] > a[type].can)
				{
					for (int i = 1; i <= k; i++)
					{
						if (f[pos-a[type].can][i] == INF) continue;
						if (i == type)
						{
							f[pos][type] = min(f[pos][type],f[pos-a[type].can][i]+a[type].waste);
						}
						f[pos][type] = min(f[pos][type],f[pos-a[type].can][i]+a[type].waste+w);
					}
				}
			}
			else
			{
				if (pos == a[type].can)
				{
					for (int i = 1; i <= k; i++)
					{
						f[pos][type] = min(f[pos][type],f[pos-a[type].can][i]+a[type].waste);
						
					}
				}
				else if (pos > a[type].can)
				{
					for (int i = 1; i <= k; i++)
					{
						if (f[pos-a[type].can][i] == INF) continue;
						if (i == type)
						{
							f[pos][type] = min(f[pos][type],f[pos-a[type].can][i]+a[type].waste);
						}
						f[pos][type] = min(f[pos][type],f[pos-a[type].can][i]+a[type].waste+w);
					}
				}
			}
		}
	}
	ll ans = INF;
	for (int i = 1; i <= k; i++)
	{
		if (ans > f[n][i]) ans = f[n][i];
	}
	if (ans == INF) ans = -1;
	out(ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值