【P4042 [AHOI2014/JSOI2014]骑士游戏】

最短路好题。
如果考虑dp的话,设\(f_i\)表示第i个怪物被消灭的最小代价,那么显然有\(f_i=min(k_i,s_i+\sum\limits_{j=1}^{r_i}f_{vj})\)
但是题目中,怪物会形成一个环,具有后效性。
这里有两种方法:

  1. 借助spfa思想,既然不知道从哪开始转移,那就全部进队,全都转移,不断进行松弛,直到队列为空。但是这里有一点需要注意,传统的spfa进行更新时,都是一对多的更新,但这个题目中的更新属于多对一的更新。因此队列中不再维护“可能更新其他点的点”,而是维护“可能被其他点更新的点”。
  2. 借助dij的思想,\(f_i\)能从第二个式子转移的必要条件是任意\(f_{vj},有f_{vj}<f_i\),于是我们可以对dp值建一个堆,每次取最小值去更新其他点,如果被更新的dp值已经更新完了,将其加入堆内;如果其需要更新的dp值已经被弹出,那么显然有那个dp值小于当前dp值所以更新无用

Dij

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define re register
#define int long long
int read() {
	char cc = getchar(); int cn = 0, flus = 1;
	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
	return cn * flus;
}
const int N = 2e5 + 5 ; 
const int M = 1e6 + 5 ; 
int n, s[N], K[N], dp[N], vis[N], ans[N], R[N] ;
vector<int> mp[N] ; 
struct node {
	int id, w ; 
	bool operator < ( const node& x ) const {
		return w > x.w ; 
	}
};
priority_queue<node> q ; 
signed main()
{
	n = read() ; int x, siz ; 
	rep( i, 1, n ) 	{
		s[i] = read(), K[i] = read(), R[i] = read() ;
		rep( j, 1, R[i] ) x = read(), mp[x].push_back(i) ; 
		q.push((node){ i, K[i] }), dp[i] = s[i] ;
	}
	while( !q.empty() ) {
		int u = q.top().id, w = q.top().w ; q.pop() ; 
		if( vis[u] ) continue ; 
		vis[u] = 1, ans[u] = w, siz = mp[u].size() - 1 ; 
		rep( i, 0, siz ) {
			int v = mp[u][i] ; 
			if( vis[v] || dp[v] > K[v] ) continue ; 
			R[v] --, dp[v] += w ;
			if( R[v] == 0 ) q.push((node){ v, dp[v] }) ;
		}
	}
	printf("%lld\n", ans[1] ) ;
	return 0;
}

Spfa

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
	register int x=0,w=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-'){ch=getchar();w=-1;}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();	}
	return x*w;
}
const int N=2e5+100;
int n,d[N],s[N];
vector<int>son[N],fa[N];
int vis[N];
void spfa()
{
	queue<int>q;
	memset(vis,1,sizeof vis);
	for(int i=1;i<=n;++i) q.push(i);
	while(q.size())
	{
		int x=q.front();q.pop();
		vis[x]=0;
		int w=s[x];
		for(int i=0;i<son[x].size();++i){
			w+=d[son[x][i]];
		}
		if(w<d[x]){
			d[x]=w;
			for(int i=0;i<fa[x].size();++i){
				int y=fa[x][i];
				if(vis[y]==0) {
					vis[y]=1;
					q.push(y);
				}
			}
		}
	}
}
void add(int x,int y)
{
     son[x].push_back(y);
     fa[y].push_back(x);
}
signed main()
{
    n=read();
    for(int i=1;i<=n;++i)
    {
    	s[i]=read();
    	d[i]=read();
    	int x=read(),y;
    	while(x--){
    		y=read();
    		add(i,y);
		}
	}
	spfa();
	cout<<d[1];
	return 0;
}