洛谷 P3627 [APIO2009]抢掠计划 tarjan缩点+SPFA

题目描述
Siruseri 城中的道路都是单向的。不同的道路由路口连接。按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机。令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。

Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫。他将从市中心 出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆 祝他的胜利。

使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额。他希 望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可 以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机 里面就不会再有钱了。 例如,假设该城中有 6 个路口,道路的连接情况如下图所示:

市中心在路口 1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表

示。每个 ATM 机中可取的钱数标在了路口的上方。在这个例子中,Banditji 能抢 劫的现金总数为 47,实施的抢劫路线是:1-2-4-1-2-3-5。

输入格式
第一行包含两个整数 N、M。N 表示路口的个数,M 表示道路条数。接下来 M 行,每行两个整数,这两个整数都在 1 到 N 之间,第 i+1 行的两个整数表示第 i 条道路的起点和终点的路口编号。接下来 N 行,每行一个整数,按顺序表示每 个路口处的 ATM 机中的钱数。接下来一行包含两个整数 S、P,S 表示市中心的 编号,也就是出发的路口。P 表示酒吧数目。接下来的一行中有 P 个整数,表示 P 个有酒吧的路口的编号。

输出格式
输出一个整数,表示 Banditji 从市中心开始到某个酒吧结束所能抢劫的最多 的现金总数。

输入输出样例
输入 #1复制
6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1
5
1 4
4 3 5 6
输出 #1复制
47
说明/提示
50%的输入保证 N, M<=3000。所有的输入保证 N, M<=500000。每个 ATM 机中可取的钱数为一个非负整数且不超过 4000。

输入数据保证你可以从市中心 沿着 Siruseri 的单向的道路到达其中的至少一个酒吧。

解法:tarjan缩点+SPFA求最短路

  • 关于每一个环,我们都可以将这个环缩成一个点,建一个新图,然后在这个新图上求最短路即可,因为让我们求最长,所以我们可以建负边,然后求最短即可

AC代码

#include<cstdio>
#include<queue>
#include<cstring>
#include<stack>
#define re register int
#define si 500010
using namespace std;
struct edge {
	int nex,from,to;
}e[si];
struct node {
	int nex,to,w;
}t[si];
int d[si],dfn[si],low[si],a[si],sum[si];
int n,m,S,P,cnt,num,tot,ans,head[si],dis[si];
queue<int> q; stack<int> s; bool v[si];
inline int read() {
	int x=0,cf=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-') cf=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar(); 
	}
	return x*cf;
}
inline void add1(int x,int y) {
	e[++cnt].from=x,e[cnt].to=y,e[cnt].nex=head[x],head[x]=cnt;
}
inline void add2(int x,int y,int z) {
	t[++cnt].to=y,t[cnt].w=z,t[cnt].nex=head[x],head[x]=cnt;
}
inline void tarjan(int x) {
	dfn[x]=low[x]=++num;
	s.push(x),v[x]=true;
	for(re i=head[x];i;i=e[i].nex) {
		int y=e[i].to;
		if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
		else if(v[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]) {
		tot++;
		while(x!=s.top()) {
			a[s.top()]=tot;
			sum[tot]+=d[s.top()];
			v[s.top()]=false; s.pop();
		}
		a[x]=tot; sum[tot]+=d[x];
		v[x]=false; s.pop();
	}
	return;
}
inline void spfa() {
	memset(dis,0x3f,sizeof(dis));
	memset(v,false,sizeof(v));
	q.push(a[S]),dis[a[S]]=-sum[a[S]],v[a[S]]=1;
	while(q.size()) {
		int x=q.front(); q.pop(); v[x]=0;
		for(re i=head[x];i;i=t[i].nex) {
			int y=t[i].to,z=t[i].w;
			if(dis[y]>dis[x]+z) {
				dis[y]=dis[x]+z;
				if(!v[y]) q.push(y),v[y]=1;
			}
		}
	}
}
int main() {
	n=read(),m=read();
	for(re i=1;i<=m;i++) {
		int x=read(),y=read(); add1(x,y);
	}
	for(re i=1;i<=n;i++) d[i]=read();
	for(re i=1;i<=n;i++) {
		if(!dfn[i]) tarjan(i);
	}
	memset(head,0,sizeof(head)); cnt=0;
	for(re i=1;i<=m;i++) {
		if(a[e[i].from]!=a[e[i].to]) {
			add2(a[e[i].from],a[e[i].to],-sum[a[e[i].to]]);
		}
	}
	S=read(),P=read(); spfa();
	for(re i=1;i<=P;i++) {
		int x=read();
		if(-dis[a[x]]>ans) ans=-dis[a[x]];
	}
	printf("%d",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值