POJ 3592 Instantaneous Transference

题目大意:

        给出一个n*m的格子地图,每一格上面是0~9,“*”或“#”。如果格子上是数字代表这个格子上有当前数量的矿石。如果是“*” 代表着当前格子是一个传送阵可以传送到指定的地方。如果是“#”代表当前格子不可达。

        现在有一个矿车在坐标(0,0),也就是左上角。他只能向右和下行驶。当遇到传送阵时可以被传送到指定的位置。当他遇到数字时就可以得到那些数量的矿石,那个地方的矿石数量就变为“0”。问矿车最多可以采多少矿。


解题思路:

1、我们先要根据格子地图建图。(注1)

2、因为建立出的图可能会有环,我们要用Tarjan算法将环缩成点。(注2)

3、我们将缩点处理后的图重新建图。(注3)

4、对图求最长路。


注意:

1、要注意到“#”和从“#”出发的边是不建立的。当从“*”出发时需要建三条边:右,下,指定位置。

2、根据题意,经过的地方将不再有矿石。所以将环缩成点是最好的选择。因为如果可以到环中的任意一点,其他点也就可以到了。

3、重新建图的目的是为后来更容易的求最长路做准备。建图时对于每一条边(u,v),边的权值是点v所在的缩点的总权值。

4、求完最长路后得出的数值一定要再加上点0所在的缩点的总权值。

5、注意不要手残(手残毁一天啊~~~ T _ T ).


下面是代码:

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

using namespace std;

const int MAXN = 2005;
struct node
{
    int u,to,next;
} edge[MAXN*10];
struct node1
{
    int v,w,next;
} newedge[MAXN*10];
int n,m,p;
int dfn[MAXN],low[MAXN],vis[MAXN],head[MAXN],stack1[MAXN],top,cnt,time;
int num[MAXN],numcnt,numw[MAXN],map1[MAXN];
int newhead[MAXN],newcnt;
int dis[MAXN];
char s[45][45];
void init()
{
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(newhead,-1,sizeof(newhead));
    memset(numw,0,sizeof(numw));
    memset(stack1,-1,sizeof(stack1));
    memset(num,-1,sizeof(num));
    top=0;
    cnt=0;
    newcnt=0;
    time=1;
    numcnt=0;
}
int min(int a,int b)
{
    if(a>b)a=b;
    return a;
}
void addedge(int u,int v)
{
    edge[cnt].u=u;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
    cnt++;
}
void Buildedge()
{
    int x,y;
    for(int i=0; i<n; i++)
    {
        scanf("%s",s[i]);
    }
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<m; j++)
        {
            if(s[i][j]=='*')
            {
                scanf("%d%d",&x,&y);
                if(s[x][y]!='#')
                    addedge(i*m+j,x*m+y);
                map1[i*m+j]=0;
                if(i+1<n&&s[i+1][j]!='#')
                {
                    addedge(i*m+j,(i+1)*m+j);
                }
                if(j+1<m&&s[i][j+1]!='#')
                {
                    addedge(i*m+j,i*m+j+1);
                }
            }
            else if(s[i][j]!='#')
            {
                map1[i*m+j]=s[i][j]-'0';
                if(i+1<n&&s[i+1][j]!='#')
                {
                    addedge(i*m+j,(i+1)*m+j);
                }
                if(j+1<m&&s[i][j+1]!='#')
                {
                    addedge(i*m+j,i*m+j+1);
                }
            }
            else
            {
                map1[i*m+j]=0;
            }
        }
    }
}
void dfs(int u,int fa)
{
    dfn[u]=time;
    low[u]=time;
    time++;
    vis[u]=1;
    stack1[top]=u;
    top++;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].to;
        if(!vis[v])
        {
            dfs(v,u);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]==1)
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        while(stack1[top]!=u&&top>0)
        {
            top--;
            num[stack1[top]]=numcnt;
            vis[stack1[top]]=2;
            numw[numcnt]+=map1[stack1[top]];
        }
        numcnt++;
    }
}
void addedgenew(int u,int v)
{
    for(int i=newhead[u]; i!=-1; i=newedge[i].next)
    {
        if(newedge[i].v==v)return;
    }
    newedge[newcnt].v=v;
    newedge[newcnt].w=numw[v];
    newedge[newcnt].next=newhead[u];
    newhead[u]=newcnt;
    newcnt++;
}
int spfa(int src)
{
    queue <int> q;
    q.push(src);
    memset(vis,0,sizeof(vis));
    vis[src]=true;
    for(int i=0; i<=numcnt; i++)
    {
        dis[i]=-1;
    }
    dis[src]=0;
    while(!q.empty())
    {
        int p,t=q.front();
        q.pop();
        p=newhead[t];
        vis[t]=false;
        while(p!=-1)
        {
            if(dis[newedge[p].v]<dis[t]+newedge[p].w)
            {
                dis[newedge[p].v]=dis[t]+newedge[p].w;
                if(!vis[newedge[p].v])
                {
                    vis[newedge[p].v]=true;
                    q.push(newedge[p].v);
                }
            }
            p=newedge[p].next;
        }
    }
    int max1=0;
    for(int i=0; i<numcnt; i++)
    {
        if(dis[i]>max1)
        {
            max1=dis[i];
        }
    }
    return max1+numw[src];
}
void Tarjan()
{
    for(int i=0; i<p; i++)
    {
        if(!vis[i])dfs(i,-1);
    }
}
void BuildAgain()
{
    for(int i=0; i<p; i++)
    {
        for(int j=head[i]; j!=-1; j=edge[j].next)
        {
            if(num[i]!=num[edge[j].to]&&num[i]!=-1&&num[edge[j].to]!=-1)
            {
                addedgenew(num[i],num[edge[j].to]);
            }
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        Buildedge();//建图
        p=n*m; //总点数
        Tarjan();//求强连通分量用来缩点
        BuildAgain();//重新建图
        printf("%d\n", spfa(num[0]));//求最长路
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值