Description
Input
第一行包含两个整数N、M。N表示路口的个数,M表示道路条数。接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号。接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数。接下来一行包含两个整数S、P,S表示市中心的编号,也就是出发的路口。P表示酒吧数目。接下来的一行中有P个整数,表示P个有酒吧的路口的编号
Output
输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数。
Sample Input
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
Sample Output
47
HINT
50%的输入保证N, M<=3000。所有的输入保证N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。
解题方法:
首先进行强连通缩点,维护每一个连通分量中,是否有一个点是酒吧,以及总钱数的多少。对缩点后的图进行拓扑排序,然后按照拓扑排序进行动态规划。记 dp[i]表示走到点 i 的最大获利,对于给定的起点属于的连通块有 dp[i]=money[i],其余点的初值都是 dp[i]=负无穷。对于一条边 i 连接 j,我们用 dp[i]+money[j]来更新 dp[j]。答案是满足一个连通块中至少有一个是酒吧的强连通分量中最大的 dp[i]。由于会爆栈,强连通分量算法要进行人工堆栈。时间复杂度 O(n+m)。
//bzoj 1179 tarjan spfa
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500005;
int n, m, q, S, dfs_clock, cnt, scc, top, ans;
int head1[maxn], head2[maxn], dp[maxn];
int dfn[maxn], low[maxn], c[maxn], v[maxn], s[maxn], belong[maxn];
bool inq[maxn], vis[maxn];
struct edge{
int v, nxt;
edge(){}
edge(int v, int nxt) : v(v), nxt(nxt) {}
}E1[maxn*2], E2[maxn*2];
void init1(){
cnt = 0;
memset(dp, 0, sizeof(dp));
memset(head1, -1, sizeof(head1));
}
void init2(){
cnt = 0;
memset(head2, -1, sizeof(head2));
}
void addedge1(int u, int v){
E1[cnt].v = v, E1[cnt].nxt = head1[u], head1[u] = cnt++;
}
void addedge2(int u, int v){
E2[cnt].v = v, E2[cnt].nxt = head2[u], head2[u] = cnt++;
}
void tarjan(int x){
int now = 0;
dfn[x] = low[x] = ++dfs_clock;
s[++top] = x; inq[x] = 1;
for(int i = head1[x]; ~i; i = E1[i].nxt){
int to = E1[i].v;
if(!dfn[to]){
tarjan(to);
low[x] = min(low[x], low[to]);
}
else if(inq[to]){
low[x] = min(low[x], dfn[to]);
}
}
if(low[x] == dfn[x]){
scc++;
while(now != x){
now = s[top]; top--;
belong[now] = scc;
v[scc] += c[now];
inq[now] = 0;
}
}
}
void rebuild(){
init2();
for(int i = 1; i <= n; i++){
for(int j = head1[i]; ~j; j = E1[j].nxt){
int to = E1[j].v;
if(belong[i] != belong[to]){
addedge2(belong[i], belong[to]);
}
}
}
}
void spfa(){
queue <int> que;
que.push(belong[S]);
vis[belong[S]] = 1;
dp[belong[S]] = v[belong[S]];
while(!que.empty()){
int now = que.front(); que.pop(); vis[now] = 0;
for(int i = head2[now]; ~i; i = E2[i].nxt){
int to = E2[i].v;
if(dp[now] + v[to] > dp[to]){
dp[to] = dp[now] + v[to];
if(!vis[to]){
vis[to] = 1;
que.push(to);
}
}
}
}
}
int main(){
init1();
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++){
int u, v;
scanf("%d%d", &u, &v);
addedge1(u, v);
}
for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
scanf("%d%d", &S, &q);
rebuild();
spfa();
for(int i = 1; i <= q; i++){
int x;
scanf("%d", &x);
if(dp[belong[x]] > ans) ans = dp[belong[x]];
}
printf("%d\n", ans);
return 0;
}