您的位置:首页 > 其它

poj 3160 Father Christmas flymouse 【SCC缩点 + 虚拟源点SPFA求最长路】

2015-08-09 19:04 477 查看
Father Christmas flymouse

Time Limit: 1000MSMemory Limit: 131072K
Total Submissions: 2971Accepted: 1007
Description

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 N distinct rooms
andM 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 jindicating 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.

差不多一个多星期没看SCC了,今天碰到与SCC有关的题目,没想到纯手写后1A了。好激动!

题意:有N个屋子以及连接屋子的M条有向边,每个屋子都有一个舒适指数(可正可负)。起始舒适指数为0,你可以任选一个屋子当作起点并且可以走一个屋子多次,但最多获取屋子的舒适指数一次(对于一个屋子的舒适指数可以选择获取或者不获取)。问你在这个过程中所能获得的最大舒适指数。
把N个屋子虚拟成N个点。

分析:若以任一个SCC里面舒适指数为非负的屋子为起点,无论怎样我们至少能获取这个SCC里面所有为正的舒适指数(要理解)。这也就意味着,每个SCC的舒适指数可以用这个SCC里面所有为正的舒适指数之和来表示。到这里,就可以把问题转化成:有向图SCC缩点后,用SCC的舒适指数建边,求以任一个SCC为起点 所能跑出的最长路。

思路:用used数组记录每个SCC的最大舒适指数,用dist数组存储虚拟源点0到节点的最远距离。

一:对有向图求出所有的SCC。

二:求出每个SCC的used值。(累加所有为正的舒适指数)

三:缩点并对每<u,v>边 建立边权used[v]。

四:设置虚拟源点0,连接所有的SCC,边权为对应SCC的used值。

五:以0为源点跑一次SPFA,对dist数组排序后,输出最大的dist值。

AC代码:
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <algorithm>
#define MAXN 150000+100
#define MAXM 300000+1000
using namespace std;
struct Edge
{
    int from, to, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int low[MAXN], dfn[MAXN];
int dfs_clock;
int sccno[MAXN], scc_cnt;
vector<int> scc[MAXN];//存储每个SCC的点
stack<int> S;//存储当前SCC的点
bool Instack[MAXN];
int N, M;
int com[MAXN];//存储进入每个房间的舒适指数
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
    Edge E = {u, v, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
void getMap()
{
    int a, b;
    for(int i = 1; i <= N; i++)
        scanf("%d", &com[i]);
    while(M--)
    {
        scanf("%d%d", &a, &b);
        a++, b++;
        addEdge(a, b);
    }
}
void tarjan(int u, int fa)
{
    int v;
    low[u] = dfn[u] = ++dfs_clock;
    S.push(u);
    Instack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if(!dfn[v])
        {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
        }
        else if(Instack[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        scc_cnt++;
        scc[scc_cnt].clear();
        for(;;)
        {
            v = S.top(); S.pop();
            Instack[v] = false;
            sccno[v] = scc_cnt;
            scc[scc_cnt].push_back(v);
            if(v == u) break;
        }
    }
}
void find_cut(int l, int r)
{
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(sccno, 0, sizeof(sccno));
    memset(Instack, false, sizeof(Instack));
    dfs_clock = scc_cnt = 0;
    for(int i = l; i <= r; i++)
        if(!dfn[i]) tarjan(i, -1);
}
struct IT//求最长路
{
    int from, to, val, next;
};
IT it[MAXM];
int head1[MAXN], edgenum1;
int used[MAXN];//存储每个SCC里面的正权舒适指数
int dist[MAXN];//源点到节点的最长距离
bool vis[MAXN];//标记是否入队
void addIT(int u, int v, int w)
{
    IT E = {u, v, w, head1[u]};
    it[edgenum1] = E;
    head1[u] = edgenum1++;
}
void suodian()//缩点建新图
{
    edgenum1 = 0;
    memset(head1, -1, sizeof(head1));
    for(int i = 0; i < edgenum; i++)
    {
        int u = sccno[edge[i].from];
        int v = sccno[edge[i].to];
        if(u != v)
            addIT(u, v, used[v]);
    }
    //设置虚拟源点 0
    for(int i = 1; i <= scc_cnt; i++)
        addIT(0, i, used[i]);
}
void SPFA(int sx)//跑最长路
{
    queue<int> Q;
    memset(dist, 0, sizeof(dist));
    memset(vis, false, sizeof(vis));
    vis[sx] = true;
    Q.push(sx);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(int i = head1[u]; i != -1; i = it[i].next)
        {
            IT E = it[i];
            if(dist[E.to] < dist[u] + E.val)
            {
               dist[E.to] = dist[u] + E.val;
               if(!vis[E.to])
               {
                   vis[E.to] = true;
                   Q.push(E.to);
               }
            }
        }
    }
}
void solve()
{
    //先统计每个SCC里面的正权舒适指数
    for(int i = 1; i <= scc_cnt; i++)
    {
        int sum = 0;
        for(int j = 0; j < scc[i].size(); j++)
        {
            if(com[scc[i][j]] > 0)
                sum += com[scc[i][j]];
        }
        used[i] = sum;
    }
    suodian();//缩点
    SPFA(0);//最长路
    sort(dist, dist + scc_cnt+1);
    printf("%d\n", dist[scc_cnt]);
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        init();
        getMap();
        find_cut(1, N);//求SCC
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: