码迷,mamicode.com
首页 > 其他好文 > 详细

模板:tarjan缩点+重新建边+最短路

时间:2017-12-16 23:17:59      阅读:334      评论:0      收藏:0      [点我收藏+]

标签:front   cstring   最短路   while   math   using   算法   允许   class   

洛谷P3387 【模板】缩点

题目背景

缩点+DP

题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

 

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

 

输出格式:

 

共一行,最大的点权之和。

 

输入输出样例

输入样例#1: 复制
2 2
1 1
1 2
2 1
输出样例#1: 复制
2

说明

n<=10^4,m<=10^5,|点权|<=1000 算法:Tarjan缩点+DAGdp

 直接上代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

int last[100100],len=0,sccno[10010]={0},scc=0,n,m,u,v,w[10010]={0};
int dfn[10010]={0},low[10010]={0},dfscnt=0,wscc[10010]={0};
int f[100010]={0},t[100010]={0},ans=-2147483648,dis[10010]={0};
bool vis[10010];

stack <int> s;

struct edge {
int next,to;
}e[100100];

int gi() {
char c=getchar();bool f=0;int a=0;
while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=1;c=getchar();}
while (c>=‘0‘&&c<=‘9‘) {a=a*10+c-‘0‘;c=getchar();}
return f?-a:a;
}

void add(int x,int y) {
e[++len].to=y;
e[len].next=last[x];
last[x]=len;
}

void init() {
memset(last,-1,sizeof(last));
n=gi();m=gi();
for (int i=1;i<=n;i++) w[i]=gi();
for (int i=1;i<=m;i++) {
u=gi();v=gi();add(u,v);f[i]=u;t[i]=v;
}
}

int tarjan(int r) {
s.push(r);
dfn[r]=low[r]=++dfscnt;
for (int i=last[r];i!=-1;i=e[i].next) {
int y=e[i].to;
if (!dfn[y]) {tarjan(y);low[r]=min(low[r],low[y]);}
else if (!sccno[y]) low[r]=min(low[r],dfn[y]);
}
if (dfn[r]==low[r]) {
scc++;
while (1) {
int x=s.top();sccno[x]=scc;wscc[scc]+=w[x];
s.pop();if (x==r) break;
}
}
}

void build() {
len=0;
memset(last,-1,sizeof(last));
for (int i=1;i<=m;i++) {
if (sccno[f[i]]!=sccno[t[i]]) add(sccno[f[i]],sccno[t[i]]);
}
}

int bfs(int x) {
memset(dis,-1,sizeof(dis));
memset(vis,false,sizeof(vis));
dis[x]=wscc[x];
queue <int> q;
vis[x]=true;q.push(x);
while (!q.empty()) {
int u=q.front();q.pop();vis[u]=false;
for (int i=last[u];i!=-1;i=e[i].next) {
int v=e[i].to;
if (dis[v]<dis[u]+wscc[v]) {
dis[v]=dis[u]+wscc[v];
if (!vis[v]) {vis[v]=true;q.push(v);}
}
}
}
for (int i=1;i<=scc;i++) ans=max(dis[i],ans);
}

int main() {
init();
for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
build();
for (int i=1;i<=scc;i++) bfs(i);
printf("%d\n",ans);
return 0;
}

飞一样的感觉,这一题我是一遍过的!

模板:tarjan缩点+重新建边+最短路

标签:front   cstring   最短路   while   math   using   算法   允许   class   

原文地址:http://www.cnblogs.com/fushao2yyj/p/8047809.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!