『毒瘤算法系列6』騎士遊戲(SPFA優化DP)

Problem\mathrm{Problem}

在遊戲中,JYY一共有兩種攻擊方式,一種是普通攻擊,一種是法術攻擊。兩種攻擊方式都會消耗JYY一些體力。採用普通攻擊進攻怪獸並不能把怪獸徹底殺死,怪獸的屍體可以變出其他一些新的怪獸,注意一個怪獸可能經過若干次普通攻擊後變回一個或更多同樣的怪獸;而採用法術攻擊則可以徹底將一個怪獸殺死。

遊戲世界中一共有NN種不同的怪獸,分別由11NN編號,現在11號怪獸入侵村莊了,JYY想知道,最少花費多少體力值才能將所有村莊中的怪獸全部殺死呢?

Solution\mathrm{Solution}

我們從DP的思想來考慮這道題,設fif_i爲殺死怪獸ii的最小代價。

則我們可以列出一個很顯然的狀態轉移方程:fi=min(Ki,Si+ijfj)f_i=\min(K_i,S_i+\sum_{i→j} f_j)

我們觀察到這是一個帶有環的DP式,我們可以用SPFA算法來解決帶有環的狀態轉移

我們可以這樣實現:

  • 將所有節點放入隊列並設置初始化KiK_i,因爲排除題目干擾我們並不能找到一個固定的起點。
  • 每一次將節點ii轉移完成以後,都將節點『殺死後產生ii的節點』入隊。

最後輸出f1f_1即可。

Code\mathrm{Code}

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int N = 5e5;

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' || c > '9') w |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

int n;
int dis[N], S[N], K[N], vis[N];
vector < int > a[N], b[N];
queue < int > q; 

void SPFA(void)
{
	for (int i=1;i<=n;++i) dis[i] = K[i];
	//for (int i=1;i<=n;++i) vis[i] = 1, q.push(i);
	vis[1] = 1, q.push(1);
	while (q.size())
	{
		int x = q.front();
		q.pop(), vis[x] = 0;
		int sum = S[x];
		for (int i=0;i<a[x].size();++i)
		{
			int y = a[x][i];
			sum += dis[y];
		}
		if (sum >= dis[x]) continue;
		dis[x] = sum;
		for (int i=0;i<b[x].size();++i)
		{
			int y = b[x][i];
			if (vis[y] == 0) {
				q.push(y);
				vis[y] = 1;
			}
		}
	} 
	return;
}

signed main(void)
{
	n = read();
	for (int x=1;x<=n;++x)
	{
		S[x] = read();
		K[x] = read();
		int r = read();
		while (r --)
		{
			int y = read();
			a[x].push_back(y);
			b[y].push_back(x);
		}
	}
	SPFA();
	cout << dis[1] << endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章