POJ 3592(强连通缩点,拓扑最长路)

题意:

     n*m矩阵,*表示传送门,矩阵后面给出其到达的点,#障碍点,不可到达,数字表示该点的宝藏数,每个点可以往下或者往右走,获得宝藏后该点宝藏数变为0,问最多能获得多少宝藏。

思路:

   建图+强连通缩点+拓扑dp。

难点:

   构图:#点不可做为起点也不可以作为终点。总共n*m个点,标号别和其他量弄混。

   强连通缩点:只对“1”所处的块tarjan即可,拓扑序同理。

AC代码:

/**Wjvje**/
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
/** -?£- **/
#define LL long long
#define par pair<LL,int>
#define INF 0x3f3f3f3f
#define io ios::sync_with_stdio(false)
using namespace std;
const int N=1600+100;
const int M=3e6+100;
int head[N];
int ver[M];
int Next[M];
int tot;
void add(int x,int y)
{
    ver[++tot]=y;
    Next[tot]=head[x];
    head[x]=tot;
}
int hc[N];
int vc[M];
int nc[M];
int tc;
int n,m;
int dfn[N],low[N];
int stackk[N],ins[N],c[N];
int num=0;
int top;//指针
int cnt;//强联通分量标号
int value2[N];
int value[N];
void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    stackk[++top]=x;
    ins[x]=1;

    for(int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if(!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y])
        {
            low[x]=min(low[x],low[y]);
        }
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        int y;
        do
        {
            y=stackk[top--];
            ins[y]=0;
            c[y]=cnt;
            value2[cnt]+=value[y];//value2缩点后,value缩点前
            //cout<<y<<".."<<value2[cnt]<<endl;

        }
        while(x!=y);
    }
}
int to[N];
int deg[N];
void add_c(int x,int y)
{
    vc[++tc]=y;
    nc[tc]=hc[x];
    hc[x]=tc;
    deg[y]++;
}
int kk;
void topsort(int nn)
{
    queue<int> q;
    kk=0;
    while(q.size())q.pop();
    for(int i=1;i<=nn;++i)
        if(deg[i]==0)q.push(i);
    while(q.size())
    {
        int x=q.front();
        q.pop();
        to[++kk]=x;
        for(int i=hc[x];i;i=nc[i])
        {
            int y=vc[i];
            if(--deg[y]==0)q.push(y);
        }
    }

}
char ch[101][101];
int mer[N];
int cal(int x,int y)
{
    return (x-1)*m+y;
}
bool judge(int x,int y)
{
    return x>=1&&x<=n&&y>=1&&y<=m;
}
bool mark[2000][2000];

int dp[N];
int main()
{

    int t;
    cin>>t;

    while(t--)
    {
        cin>>n>>m;
        kk=0;
        memset(dp,0,sizeof(dp));
        memset(ch,'0',sizeof(ch));
        memset(to,0,sizeof(to));
        memset(value,0,sizeof(value));
        memset(value2,0,sizeof(value2));
        memset(mer,0,sizeof(mer));
        memset(head,0,sizeof(head));
        memset(Next,0,sizeof(Next));
        memset(ver,0,sizeof(ver));
        memset(ins,0,sizeof(ins));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(stackk,0,sizeof(stackk));
        memset(c,0,sizeof(c));
        tot=1;
        memset(hc,0,sizeof(hc));
        memset(nc,0,sizeof(nc));
        memset(vc,0,sizeof(vc));
        memset(mark,0,sizeof(mark));
        tc=1;
        num=cnt=top=0;
        int tmp=0;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                cin>>ch[i][j];
                if(ch[i][j]=='*')mer[++tmp]=cal(i,j);//记录传送门
            }
        }
        for(int i=1;i<=tmp;++i)
        {
            int x,y;
            cin>>x>>y;
            x+=1;
            y+=1;
            int index=cal(x,y);
            if(ch[x][y]!='#')
            {
                add(mer[i],index);
                mark[mer[i]][index]=1;
            }
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                if(ch[i][j]=='#')continue;
                else
                {
                    if(ch[i][j]=='*')value[cal(i,j)]=0;//特判
                    else value[cal(i,j)]=ch[i][j]-'0';
                    int index=cal(i,j);
                    if(judge(i+1,j)&&mark[index][cal(i+1,j)]==0&&ch[i+1][j]!='#')add(index,cal(i+1,j));
                    if(judge(i,j+1)&&mark[index][cal(i,j+1)]==0&&ch[i][j+1]!='#')add(index,cal(i,j+1));

                }
            }
        }
        tarjan(1);

        for(int i=1; i<=n; ++i)
            for(int j=1;j<=m;++j)
            {
                int index=cal(i,j);
                if(dfn[index]==0)continue;//不属于1的块
                if(ch[i][j]=='#')continue;
                for(int k=head[index]; k; k=Next[k])
                {
                    int y=ver[k];
                    if(dfn[y]==0)continue;
                    if(c[index]==c[y])continue;
                    add_c(c[index],c[y]);
                }
            }
        //cout<<cnt<<endl;
        topsort(cnt);
        int pos=0;
        for(int i=1;i<=cnt;++i)
        {
            if(to[i]==c[1])
            {
                pos=i;
                break;
            }
        }
        dp[to[pos]]=value2[to[pos]];
        int ans=dp[to[pos]];

        //cout<<ans<<endl;
        for(int i=pos;i<=cnt;++i)
        {
            int x=to[i];
            //cout<<x<<endl;
            for(int j=hc[x];j;j=nc[j])
            {
                int y=vc[j];
                dp[y]=max(dp[y],dp[x]+value2[y]);
                ans=max(ans,dp[y]);
            }
        }
        cout<<ans<<endl;


    }

    return 0;
}

 

The end;

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值