cunzai_zsy0531

关注我

P4042 [AHOI2014/JSOI2014]骑士游戏 题解

题面

dp有后效性,转移看作边之后会成环。这时候一般需要建出图来跑最短路。

这道题的最短路有些不一样,方程为 \(f_i=\max(a_i,b_i+\sum\limits_j f_j)\),比较像最短路的松弛操作。spfa每次从队列里取出 \(u\) 来之后,遍历所有相邻点 \(v\),记录一个和,如果 \(u\) 点答案被更新了,那么应该需要入队所有有边到 \(u\) 的点 \(v\)。由于边数是 \(10^6\),spfa很危,但是还是通过了(出题人没卡好评)。

点击查看代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#define pb push_back
using namespace std;
typedef long long ll;
inline ll rd(){
	ll res=0;char c=getchar();
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())res=(res<<1)+(res<<3)+(c-'0');
	return res;
}
const int N=2e5+13,M=1e6+13;
struct Edge{int v,nxt;}e[M];
int n,h[N],tot;
inline void add_edge(int u,int v){e[++tot]=(Edge){v,h[u]};h[u]=tot;}
ll a[N],b[N],dist[N];
vector<int> g[N];
bool vis[N];
queue<int> q;
inline void spfa(){
	for(int i=1;i<=n;++i) dist[i]=a[i],vis[i]=1,q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();vis[u]=0;
		int l=g[u].size();ll sum=b[u];
		for(auto v:g[u]) sum+=dist[v];
		if(sum>=dist[u]) continue;
		dist[u]=sum;
		for(int i=h[u];i;i=e[i].nxt){
			int v=e[i].v;
			if(!vis[v]) q.push(v),vis[v]=1;
		}
	}
	printf("%lld\n",dist[1]);
}
int main(){
	n=rd();
	for(int i=1,l,x;i<=n;++i){
		b[i]=rd(),a[i]=rd(),l=rd();
		while(l--) x=rd(),g[i].pb(x),add_edge(x,i);
	}
	spfa();
	return 0;
}
posted @ 2022-05-19 16:51  cunzai_zsy0531  阅读(21)  评论(0编辑  收藏  举报