POJ 3592 Instantaneous Transference 强连通缩点+dp最长路

题目链接:

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

题意

在一个n*m的方格中,方格可以填三种类型的字符,
1.数字
2.’*’
3.’#’
数字代表他到达这儿所能获得的价值,*表示这点为传输机,走到这儿可以选择传送与它绑定的某个点,#代表墙壁,不能走到这儿。一个人从左上角走到右下角,在每个点只能往下或往右,在传输机位置处,可以选择传送或不传送,每个点的价值只能获得一次,问这个人能获得的最大价值。

思路

按照题中描叙的方式建图,缩点后形成dag。然后求dag上的最长路。
容易错的地方:起点(左上角)所在的连通分量的dp值为最终答案。这道题真的要细心,太容易出错了。

#include<cstdio>
#include<queue>
#include<iostream>
#include<vector>
#include<map>
#include<cstring>
#include<string>
#include<set>
#include<stack>
#include<algorithm>
#define cle(a) memset(a,0,sizeof(a))
#define inf(a) memset(a,0x3f,sizeof(a))
#define ll long long
#define Rep(i,a,n) for(int i=a;i<=n;i++)
using namespace std;
const int INF = ( 2e9 ) + 2;
const ll maxn =  1700;
vector<int> g[maxn],dag[maxn];
char mp[50][50];
int time,num;
int dfn[maxn],low[maxn],instack[maxn],scc[maxn],w[maxn],w2[maxn];
int dp[maxn];
stack<int> s;
void tarjan(int u)
{
    dfn[u]=low[u]=++time;
    instack[u]=1;
    s.push(u);
    for(int i=0,L=g[u].size();i<L;i++)
    {
        int v=g[u][i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u])
    {
        num++;
        int x;
        do
        {
            x=s.top();s.pop();
            w2[num]+=w[x];
            instack[x]=0;
            scc[x]=num;
        }while(x!=u);
    }
}
int find(int u)
{
    if(dp[u]!=-INF)return dp[u];
    dp[u]=w2[u];
    for(int i=0;i<dag[u].size();i++)
    {
        int v=dag[u][i];
        dp[u]=max(dp[u],w2[u]+find(v));
    }
    return dp[u];
}
int main()
{
    int t;
//  freopen("in.txt","r",stdin);
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        time=num=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(w,0,sizeof(w));
        memset(w2,0,sizeof(w2));
        memset(scc,-1,sizeof(scc));
        while(!s.empty())s.pop();
        scanf("%d%d",&n,&m);
        for(int i=0;i<=(n*m);i++)
        {
            g[i].clear();
            dag[i].clear();
        }
        for(int i=0;i<n;i++)
        scanf("%s",mp[i]);
        int cnt=0;
        for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            int t=i*m+j;
            if(mp[i][j]>='0'&&mp[i][j]<='9')
            w[t]=mp[i][j]-'0';
            else if(mp[i][j]=='#')
            dfn[t]=1;

            if(i>0&&mp[i-1][j]!='#'&&mp[i][j]!='#')
            {
                int l=m*(i-1)+j;
                g[l].push_back(t);
            }
            if(j>0&&mp[i][j-1]!='#'&&mp[i][j]!='#')
            {
                int l=m*i+j-1;
                g[l].push_back(t);
            }
            if(mp[i][j]=='*')
            {
                int x,y;
                scanf("%d%d",&x,&y);
                int c=x*m+y;
                if(mp[x][y]!='#')
                g[t].push_back(c);
            }
        }


        for(int i=0;i<(n*m);i++)
        if(!dfn[i])tarjan(i);

        for(int i=0;i<n*m;i++)
        {
            for(int j=0,L=g[i].size();j<L;j++)
            if(scc[i]!=scc[g[i][j]])
            {
                dag[scc[i]].push_back(scc[g[i][j]]);
            }
        }

        int ans=-INF;

        for(int i=0;i<=num;i++)dp[i]=-INF; 

        for(int i=1;i<=num;i++)
            find(i);
        ans=dp[scc[0]];
        printf("%d\n",ans);
    }
}

另外还写了个数据生成的程序,对拍时发现网上一些ac代码竟然有些数据过不了

#include<cstdio>
#include<queue>
#include<iostream>
#include<vector>
#include<map>
#include<time.h>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<set>
#include<stack>
#include<algorithm>
#define cle(a) memset(a,0,sizeof(a))
#define inf(a) memset(a,0x3f,sizeof(a))
#define ll long long
#define Rep(i,a,n) for(int i=a;i<=n;i++)
using namespace std;
const int INF = ( 2e9 ) + 2;
int main()
{
    srand(time(NULL));
    freopen("in.txt","w",stdout);
    int t=rand()%20;
    printf("%d\n",t);
    while(t--)
    {
        int n,m;
        n=rand()%10+1;
        m=rand()%10+1;
        printf("%d %d\n",n,m);
        int cnt=0;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                int f=rand()%7;
                int t=rand()%10;
                if(f==0)
                {
                    printf("%c",'*');
                    cnt++;
                }
                else if(f==1)
                    printf("%c",'#');
                    else
                        printf("%c",t+'0');
            }
            printf("\n");
        }
        for(int i=0; i<cnt; i++)
        {
            int x,y;
            x=rand()%n;
            y=rand()%m;
            printf("%d %d\n",x,y);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值