POJ 3592 Instantaneous Transference(强联通分量 Tarjan)

http://poj.org/problem?id=3592

题意 :给你一个n*m的矩阵,每个位置上都有一个字符,如果是数字代表这个地方有该数量的金矿,如果是*代表这个地方有传送带并且没有金矿,可以传送到指定的位置,如果是#代表该位置不可走,初始位置在左上角,只能向下或向右走,并且走到传送带的时候可选择是否传送。问当走出去的时候能获得的最大近况数是多少。

思路 :先将二维矩阵转化成一维的点建图,可以向下向右建图,而且传送带也可以建边,缩点之后再建边,最后用spfa求最长路。

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>

using namespace std;

const int maxn = 555555 ;
const int INF = -1000000000 ;

int belong[maxn],instack[maxn],dfn[maxn],low[maxn],g[maxn],map[maxn],c[maxn] ;
//belong[i]指的是i点所在的联通分量的编号。instack模拟的是栈,因为每个节点是要入栈的,来判断是否属于同一个分量
//dfn[i]为搜索i结点的次序编号,Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。
//g[i]是指将一个矩阵从0到n*m编号之后的,i点所在的那个位置的宝藏数,map[i]记录的是第i个传送带的位置
//c[i]记录的是第i个联通分量中宝藏的总数。
int cntt,cnt,top,bcc_clock,cntb,n,m,num ;
int head1[maxn] ,head[maxn];
char point[100][100] ;//输入的地图
int dis[maxn],coun[maxn],index ;
bool vis[maxn],flag[maxn] ;
int dire[2][2] = {{0,1},{1,0}} ;

struct node
{
    int u,v,w,next ;
} p[maxn] ,ch[maxn];

void addedge(int u,int v)
{
    p[cnt].u = u ;
    p[cnt].v = v ;
    p[cnt].next = head[u] ;
    head[u] = cnt++ ;
}

void addnode(int u,int v,int w)
{
    ch[cntt].u = u ;
    ch[cntt].v = v ;
    ch[cntt].w = w ;
    ch[cntt].next = head1[u] ;
    head1[u] = cntt++ ;
}

void tarjan(int u)
{
    vis[u] = true ;
    dfn[u] = low[u] = ++bcc_clock ;
    instack[++top] = u ;
    for(int i = head[u] ; i+1 ; i = p[i].next)
    {
        int v = p[i].v ;
        if(!dfn[v])
        {
            tarjan(v) ;
            low[u] = min(low[u],low[v]) ;
        }
        else if(vis[v])
            low[u] = min(low[u],dfn[v]) ;
    }
    if(dfn[u] == low[u])
    {
        cntb++ ;
        int v ;
        do
        {
            v = instack[top--] ;
            vis[v] = false ;
            belong[v] = cntb ;
        }
        while(v != u) ;
    }
}

void Init()
{
    memset(dfn,0,sizeof(dfn)) ;
    memset(low,0,sizeof(low)) ;
    memset(belong,0,sizeof(belong)) ;
    memset(c,0,sizeof(c)) ;
    memset(vis,0,sizeof(vis)) ;
num = 0 ;
    memset(head,-1,sizeof(head)) ;
    memset(head1,-1,sizeof(head1)) ;
    cnt = 0,top = 0 ,cntb = 0,bcc_clock = 0,cntt = 0 ;
}

bool relax(int u,int v,int w)
{
    if(dis[v] < dis[u] + w)
    {
        dis[v] = dis[u] + w ;
        return true ;
    }
    return false ;
}

bool spfa(int u)
{
    memset(flag,false,sizeof(flag)) ;
    memset(coun,0,sizeof(coun)) ;
    flag[u] = true ;
    for(int i = 0 ; i <= cntb ; i++)
        dis[i] = INF ;
    queue<int >Q ;
    Q.push(u) ;
    dis[u] = 0 ;
    while(!Q.empty ())
    {
        int st = Q.front() ;
        Q.pop() ;
        flag[st] = false ;
        for(int i = head1[st] ; i+1 ; i = ch[i].next)
        {
            if(relax(st,ch[i].v,ch[i].w) && !flag[ch[i].v])
            {
                if((++coun[ch[i].v]) > m*n) return false ;
                Q.push(ch[i].v) ;
                flag[ch[i].v] = true ;
            }
        }
    }
    index = 0 ;
    for(int i = 1 ; i <= cntb ; i++)
        index = max(index,dis[i]) ;
    return true ;
}
int main()
{
    int T ;
    scanf("%d",&T) ;
    while(T--)
    {
        scanf("%d %d",&n,&m) ;
        int cnnt = 0;//记录传送带的个数
        Init() ;
        getchar() ;
        for(int i = 0 ; i < n ; i++)
            scanf("%s",point[i]) ;
        for(int i = 0 ; i < n ; i++)
        {
            for(int j = 0 ; j < m ; j++)
            {
                int k = i*m+j ;
                if(point[i][j] == '#')
                {
                    g[k] = -1 ;
                    continue ;
                }
                else
                {
                    if(point[i][j] == '*')
                    {
                        map[cnnt++] = k ;
                        g[k] = 0 ;
                    }
                    else if(point[i][j] >= '0' && point[i][j] <= '9')
                        g[k] = point[i][j] - '0' ;
                    for(int ii = 0 ; ii < 2 ; ii++)
                    {
                        int xx = i+dire[ii][0] ;
                        int yy = j+dire[ii][1] ;
                        if(xx < n && yy < m)
                        {
                            if(point[xx][yy] != '#')
                                addedge(k,xx*m+yy) ;
                        }
                    }
                }
            }
        }
        for(int i = 0 ; i < cnnt ; i++)
        {
            int x,y ;
            scanf("%d %d",&x,&y) ;
            if(point[x][y] != '#')
                addedge(map[i],x*m+y) ;
        }
       // Init() ;
        for(int i = 0 ; i < n*m ; i++)
            if(!dfn[i]) tarjan(i) ;
        for(int i = 0 ; i < n*m ; i++)
            c[belong[i]] += g[i] ;
        addnode(0,belong[0],c[belong[0]]);//缩点之后的建边
        for(int i = 0 ; i < n*m ; i++)
        {
            for(int j = head[i] ; j + 1 ; j = p[j].next)
            {
                int v = p[j].v ;
                if(belong[i] != belong[v])
                {
                    addnode(belong[i],belong[v],c[belong[v]]) ;//两个点不属于同一个联通分量
                }
            }
        }
        spfa(0) ;//求最长路
        printf("%d
",index) ;
    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/luyingfeng/p/3553645.html