强连通+缩点+最短路

After retirement as contestant from WHU ACM Team, flymouse volunteered to do the odds and ends such as cleaning out the computer lab for training as extension of his contribution to the team. When Christmas came, flymouse played Father Christmas to give gifts to the team members. The team members lived in distinct rooms in different buildings on the campus. To save vigor, flymouse decided to choose only one of those rooms as the place to start his journey and follow directed paths to visit one room after another and give out gifts en passant until he could reach no more unvisited rooms.

During the days on the team, flymouse left different impressions on his teammates at the time. Some of them, like LiZhiXu, with whom flymouse shared a lot of candies, would surely sing flymouse’s deeds of generosity, while the others, like snoopy, would never let flymouse off for his idleness. flymouse was able to use some kind of comfort index to quantitize whether better or worse he would feel after hearing the words from the gift recipients (positive for better and negative for worse). When arriving at a room, he chould choose to enter and give out a gift and hear the words from the recipient, or bypass the room in silence. He could arrive at a room more than once but never enter it a second time. He wanted to maximize the the sum of comfort indices accumulated along his journey.

Input

The input contains several test cases. Each test cases start with two integers N and M not exceeding 30 000 and 150 000 respectively on the first line, meaning that there were N team members living in Ndistinct rooms and M direct paths. On the next N lines there are N integers, one on each line, the i-th of which gives the comfort index of the words of the team member in the i-th room. Then follow M lines, each containing two integers i and j indicating a directed path from the i-th room to the j-th one. Process to end of file.

Output

For each test case, output one line with only the maximized sum of accumulated comfort indices.

Sample Input

2 2
14
21
0 1
1 0

Sample Output

35

Hint

32-bit signed integer type is capable of doing all arithmetic.

题目大意:给你n个点,m条边,接下来n行每行一个数代表每个点的点权值,接下来m行每行零个数x,y表示从x到y有一条有向边。问我们找一条路径(任意起点出发),通过这条路径上的所有点权值加和,使得这条路径和最大。【注意:这条路径上的点可以考虑留下或者不留下,如果留下就可以拿走权值且只能拿一次,如果不留下就继续向前走,不拿这个点权值】
思路:

1、首先图可能存在有向环,也就是强连通分量,那么我们需要缩点染色,这里就要用到Tarjan算法或者是Kosaraju算法,因为Tarjan算法实现起来更方便快捷一点,而且只有一次DFS,所以这里我们写出Tarjan求强连通缩点染色的代码。

2、对于这个点留和不留的问题,其实不用考虑过分复杂,只要是正的权值,我们就拿上,因为我们求的是最大值,所以如果点权值为负,我们直接将权值赋为0就行,拿了就相当于没拿 ,所以我们这个时候都拿就行了。

3、缩点染色之后保证了图是一个DAG图,然后我们在DAG图上求一下最长路即可。这里使用SPFA实现。

4、因为在不知道图是否整个连通的时候捏,我们还是设一个超级源点为好,这样,我们就实现了起点的选取,然后求一遍SPFA就能得到结果!
 

#include<iostream>
#include<string.h>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
int tt,n,m,sig,cnt;
const int maxn=11111;
vector<int >mp[maxn];
vector<int >mp2[maxn];
int dis[maxn];
int vis[maxn];
int color[maxn];
int low[maxn];
int dfn[maxn];
int val[maxn];
int val2[maxn];
stack<int>q;
void init()
{
    for(int i=1;i<=n;i++)
		mp[i].clear(),mp2[i].clear();
    memset(val2,0,sizeof(val2));
    memset(vis,0,sizeof(vis));
    memset(color,0,sizeof(color));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
}
void SPFA(int ss)
{
    memset(vis,0,sizeof(vis));
    queue<int >s;
    s.push(ss);
    for(int i=0;i<=sig;i++)dis[i]=0;
    vis[ss]=1;
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
		vis[u]=0;
        for(int j=0;j<mp2[u].size();j++)
        {
            int v=mp2[u][j];
            if(dis[v]<dis[u]+val2[v])
            {
                dis[v]=dis[u]+val2[v];
                if(vis[v]==0)
                {
                    s.push(v);
                }
            }
        }
    }
    int output=0;
    for(int i=1;i<=sig;i++)
    {
        output=max(dis[i],output);
    }
   cout<<output<<endl;
}
void Tarjan(int u)
{
    vis[u]=1;
    low[u]=dfn[u]=cnt++;
    q.push(u);
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(vis[v]==0)
        {
            Tarjan(v);
        }
        if(vis[v]==1)low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u])
    {
        sig++;
        while(true)
        {
			int x=q.top();
			q.pop();
            color[x]=sig;
            vis[x]=2;
			if(x==u)
				break;
        }
    }
}
void Slove()
{
	sig=0;
	cnt=1;
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==0)
        Tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        val2[color[i]]+=val[i];
    }
    for(int i=1;i<=sig;i++)
    {
        mp2[0].push_back(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<mp[i].size();j++)
        {
            int v=mp[i][j];
            if(color[i]!=color[v])
            {

                mp2[color[i]].push_back(color[v]);
            }
        }
    }
    SPFA(0);
}
int main()
{
    while(cin>>n>>m)
    {
        init();
        for(int i=1;i<=n;i++)
        {
            cin>>val[i];
            if(val[i]<0)val[i]=0;
        }
        for(int i=0;i<m;i++)
        {
            int x,y;
            cin>>x>>y;
            x++;
			y++;
            mp[x].push_back(y);
        }
        Slove();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值