九野GG的题意:
题意:
给定n个点 m条有向边的图
每个点的点权
问:
遍历一遍图能得到的最大点权(对于经过的点,可以选择是否获得该点点权,但每个点只能被获得一次)
起点可以任意。
思路:
我们把有向图缩点为有向的缩点树,则某一强连通块的权值就是该连通块下的 所有正点权值和
这样我们就可以得到一个有向无环图,显然我们选择的起点是入度为0 的点,因为所有入度不为0的点 都能从别的点走过来。
那么直接从in[i]==0的点跑一下spfa,求出最长路,bfs的过程顺便dp一下就行了。
这题我起码交了20遍,思路没错,可是一直WA,WA到死,最后起死回生,推到重写了两遍就过了。
/****************************
* author:crazy_石头
* date:2014/01/18
* algorithm:tarjan+spfa
* Pro:POJ 3160
***************************/
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
using namespace std;
#define INF 1<<29
#define eps 1e-8
#define A system("pause")
#define rep(i,h,n) for(int i=(h);i<=(n);i++)
#define ms(a,b) memset((a),(b),sizeof(a))
const int maxn=30000+10;
const int maxm=150000+10;
struct edge
{
int to,next;
}e[maxm];
int dfn[maxn],low[maxn],belong[maxn],stack[maxn],in[maxn],head1[maxn],head2[maxn],scc,top,index,n,m,cnt;
bool instack[maxn],vis[maxn];
int dp[maxn],sum[maxn],w[maxn];
inline void addedge(int u,int v,int head[])
{
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
inline void tarjan(int u)
{
int v;
dfn[u]=low[u]=++index;
stack[++top]=u,instack[u]=1;
for(int i=head1[u];~i;i=e[i].next)
{
v=e[i].to;
if(dfn[v]==-1)
{
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
scc++;
do
{
v=stack[top--];
instack[v]=0;
belong[v]=scc;
sum[scc]+=w[v];
}while(u!=v);
}
}
inline void init()
{
int u,v;
scc=index=top=cnt=0;
ms(dfn,-1),ms(instack,0),ms(head1,-1),ms(head2,-1),ms(sum,0);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
if(w[i]<0)w[i]=0;
}
while(m--)
{
scanf("%d%d",&u,&v);
addedge(u+1,v+1,head1);//下标从1开始;
}
rep(i,1,n) if(dfn[i]==-1) tarjan(i);
}
inline void Rebuild()
{
int i,j,v;
ms(in,0);
rep(u,1,n)
for(i=head1[u];~i;i=e[i].next)
{
v=e[i].to;
if(belong[u]!=belong[v])
{
addedge(belong[u],belong[v],head2);
in[belong[v]]++;
}
}
}
inline int spfa(int s)
{
queue<int> q;
ms(vis,0);
q.push(s),vis[s]=1;
int res=dp[s]=sum[s];
while(!q.empty())
{
int u=q.front();
q.pop(),vis[u]=0;
for(int i=head2[u];~i;i=e[i].next)
{
int v=e[i].to;
dp[v]=max(dp[v],dp[u]+sum[v]);
res=max(res,dp[v]);
if(!vis[v])q.push(v),vis[v]=1;
}
}
return res;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
int i,ans=0;
ms(dp,0);
Rebuild();//缩点后重新构图;
for(i=1;i<=scc;i++)if(in[i]==0)ans=max(ans,spfa(i));//从入度为0的点处跑一边spfa求一遍最长路;
printf("%d\n",ans);
}
return 0;
}