pku 3592 Instantaneous Transference tarjan缩点重建图+spfa求最长路

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

/*

题意:给定一个n*m的矩阵,你从左上角出发,规定只能往当前点的右边或者下边走,其中还有一些特殊点*具有特殊的力量可以把你传到特定的一个点(你可以选择传送也可以选择不传送),问从左上角出发到不能走下去,最多能获得的矿石量(每个方格对应着一个数字表示矿石数量)。点#直接跳过

思路:首先build1根据题意描述,见图,将二位矩阵转化为一维的点建图,每个点可以向右向下建立有向边,点*还可以向传送点建边。建完后tarjan缩点,然后在根据缩点后的点建图,添加超级源点s,权值为i-j sum[j],  求最长路即可的结果;

中间数组开成了44贡献了几次wa,转化为1维后是40*40了。。。

*/

#include <cstdio>

#include <cstring>

#include <iostream>

#include <queue>

#define maxn 44

#define N 2002

using namespace std;



int dir[2][2] = {{0,1},{1,0}};

const int inf = 99999999;



struct node

{

    int v,w;

    int next;

}g1[N*N],g2[N*N];



int head1[N],head2[N],ct1,ct2;

int low[N],dfn[N],stack[N],val[N];

int bcnt,top,index,num[maxn][maxn],kpos[N];

int belong[N],sum[N],dis[N];

bool inq[N],ins[N];

int n,m,ct,knum,s;

char str[maxn][maxn];





void add1(int u,int v)

{

    g1[ct1].v = v;

    g1[ct1].next = head1[u];

    head1[u] = ct1++;

}

void add2(int u,int v,int w)

{

    g2[ct2].v = v;

    g2[ct2].w  =w;

    g2[ct2].next = head2[u];

    head2[u] = ct2++;

}

void tarjan(int i)

{

    int k,j;

    low[i] = dfn[i] = ++index;

    ins[i] = true;

    stack[++top] = i;

    for (k = head1[i]; k != -1; k = g1[k].next)

    {

        int j = g1[k].v;

        if (!dfn[j])

        {

            tarjan(j);

            low[i] = min(low[i],low[j]);

        }

        else if (ins[j])

        {

            low[i] = min(low[i],dfn[j]);

        }

    }

    if (dfn[i] == low[i])

    {

        bcnt++;

        do

        {

            j = stack[top--];

            ins[j] = false;

            belong[j] = bcnt;

        }while (j != i);

    }

}

void build1()

{

    int i,j,k,x,y;

    memset(num,0,sizeof(num));

    memset(kpos,0,sizeof(kpos));

    memset(val,0,sizeof(val));

    ct = knum = 0;

    scanf("%d%d",&n,&m);

    for (i = 0; i < n; ++i)

    {

        scanf("%s",str[i]);

        for (j = 0; j < m; ++j)

        {

            if (str[i][j] == '#') continue;

            num[i][j] = ++ct;

            if (str[i][j] >= '0' && str[i][j] <= '9')

            val[num[i][j]] = str[i][j] - '0';

            else

            {

                kpos[knum++] = num[i][j];//记录每个*点,应为后边依次输入其传输的坐标

                val[num[i][j]] = 0;

            }

        }

    }

    memset(head1,-1,sizeof(head1));

    ct1 = 0;

    for (i = 0; i < n; ++i)

    {

        for (j = 0; j < m; ++j)

        {

            if (str[i][j] == '#') continue;

            for (k = 0; k < 2; ++k)

            {

                int tx = i + dir[k][0];

                int ty = j + dir[k][1];

                if (tx >= 0 && tx < n && ty >= 0 && ty < m)

                add1(num[i][j],num[tx][ty]);

            }

        }

    }

     //根据*点传输的坐标添加边

    for (i = 0; i < knum; ++i)

    {

        scanf("%d%d",&x,&y);

        add1(kpos[i],num[x][y]);

    }



    memset(dfn,0,sizeof(dfn));

    memset(low,0,sizeof(low));

    memset(ins,false,sizeof(ins));

    memset(belong,0,sizeof(belong));

    top = index = bcnt = 0;

    for (i = 1; i <= ct; ++i)

    {

        if (!dfn[i]) tarjan(i);//tarjan缩点

    }

    //缩点后计算权值

    memset(sum,0,sizeof(sum));

    for (i = 1; i <= ct; ++i)

    sum[belong[i]] += val[i];

}

void build2()

{

    int i,k;

    s = 0;

    memset(head2,-1,sizeof(head2));//加入超级源点

    ct2 = 0;

    add2(s,belong[1],sum[belong[1]]);

    for (i = 1; i <= ct; ++i)

    {

        for (k = head1[i]; k != -1; k = g1[k].next)

        {

            int j = g1[k].v;

            if (belong[i] != belong[j])

            {

                add2(belong[i],belong[j],sum[belong[j]]);

            }

        }

    }

}

void spfa(int s)

{

    int i;

    queue<int>q;

    for (i = 0; i < N; ++i)

    {

        dis[i] = -inf;

        inq[i] = false;

    }

    q.push(s); dis[s] = 0;

    inq[s] = true;

    while (!q.empty())

    {

        int u = q.front(); q.pop();

        inq[u] = false;

        for (i = head2[u]; i != -1; i = g2[i].next)

        {

            int v = g2[i].v;

            if (dis[v] < dis[u] + g2[i].w)

            {

                dis[v] = dis[u] + g2[i].w;

                if (!inq[v])

                {

                    inq[v] = true;

                    q.push(v);

                }

            }

        }

    }

}

void solve()

{

    spfa(s);//求最长路

    int ans = 0;

    for (int i = 0; i <= bcnt; ++i)

    ans = max(ans,dis[i]);

    printf("%d\n",ans);

}

int main()

{

    int t;

    scanf("%d",&t);

    while (t--)

    {

        build1();//根据题意建图

        build2();//缩点后建图

        solve();//求解

    }

    return 0;

}

  

你可能感兴趣的:(SPFA)