poj 3592 强连通分量+最长路(spfa或者dp)(Instantaneous Transference)

题意:一辆坦克从N*M矩阵的左上角出发,每次往右或往下走一格,每格可以是'#'(表示不可以走),'*'表示传送门,或者是数字,表示在该格可以获得的值(只能取一次),传送门可以将到达该处的坦克传送到指定位置,你可以选择被传送或者走相邻的格,问坦克可以获得的值的和最大为多少。

思路:N*M矩阵上每个点看成图中的一个点,能够走的连边。那么显然在一个强连通分量里的点的值是全部能够得到的。那么思路就是先求强连通分量,然后缩点(此时点上的权值是原图所包含点得权值之和)。进而求一条从左上角点所在强连通分量出发的最长路。可以用spfa来求,也可以用dp的思想,做dp的时候对点的处理顺序蕴含了拓扑排序的思想。

写dp的时候wa了很多次,是在建新图时的入度数组写错了。有重边时在新图中也只有一条边,所以d数组也只增加1,需要注意。。

版本1:dp

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 42
char s[N][N];
int T,n,m,w[N*N],t[N*N],res;//w是原始价值,t是缩点后的价值
struct edge{
    int y,next;
}e[N*N*3];
int first[N*N],top;
int strong[N*N],dfn[N*N],low[N*N],stack[N*N],inst[N*N],con,idx,ts;
int id(int x,int y){
    return x*m+y;
}
int g[N*N][N*N],d[N*N],dp[N*N];
void init(){
    clc(first, -1);
    clc(dfn, -1);
    clc(inst, 0);
    clc(t,0);
    clc(g, 0);
    clc(strong, 0);
    clc(d, 0);
    clc(dp, 0);
    clc(w,0);
    top = con = idx = ts = res = 0;
}
void add(int x,int y){
    e[top].y = y;
    e[top].next = first[x];
    first[x] = top++;
}
void tarjan(int x){
    dfn[x] = low[x] = ++idx;
    stack[ts++] = x;
    inst[x] = 1;
    for(int i = first[x];i!=-1;i=e[i].next){
        if(dfn[e[i].y] == -1){
            tarjan(e[i].y);
            low[x] = min(low[x],low[e[i].y]);
        }else if(inst[e[i].y])
            low[x] = min(low[x],dfn[e[i].y]);
    }
    if(dfn[x] == low[x]){
        con++;
        do{
            strong[stack[--ts]] = con;
            inst[stack[ts]] = 0;
            t[con] += w[stack[ts]];
        }while(x != stack[ts]);
    }
}
int main(){
    scanf("%d",&T);
    while (T--) {
        int i,j,a,b,now;
        init();
        scanf("%d %d",&n,&m);
        //clc(s,'#');
        for(i = 0;i<n;i++)
            scanf("%s",s[i]);
        for(i = 0;i<n;i++)
            for(j = 0;j<m;j++){
                if(s[i][j] == '#')
                    continue;
                if(i+1<n && s[i+1][j] != '#')
                    add(id(i,j) , id(i+1,j));
                if(j+1<m && s[i][j+1] != '#')
                    add(id(i,j) , id(i,j+1));
                if(s[i][j] == '*'){
                    scanf("%d %d",&a,&b);
                    if(s[a][b] != '#')//WA点:要判断传送的终点位置是否可行
                        add(id(i,j),id(a,b));
                }else
                    w[id(i,j)] = s[i][j]-'0';
            }
        tarjan(0);
        for(i = 0;i<n*m;i++){
            if(strong[i] == 0)
                continue;
            for(j = first[i];j!=-1;j=e[j].next)
                if(strong[e[j].y] && strong[i] != strong[e[j].y]){
                    g[strong[i]][strong[e[j].y]]++;
                    if(g[strong[i]][strong[e[j].y]] == 1)//这个地方没有注意WA了很多次。。。(因为建新图时忽略重边的话d数组就错了)
                        d[strong[e[j].y]]++;
                }
        }
        queue<int> q;
        q.push(strong[0]);
        dp[strong[0]] = t[strong[0]];
        while(!q.empty()){
            now = q.front();
            q.pop();
            for(i = 1;i<=con;i++){
                if(g[now][i] && d[i]>=1){
                    dp[i] = max(dp[i],dp[now]+t[i]);
                    d[i]--;
                    if(!d[i])
                        q.push(i);
                }
            }
        }
        for(i = 1;i<=con;i++)
            res = max(res,dp[i]);
        printf("%d\n",res);
    }
    return 0;
}

版本2:spfa

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 42
char s[N][N];
int T,n,m,w[N*N],t[N*N],res;//w是原始价值,t是缩点后的价值
struct edge{
    int y,next;
}e[N*N*3];
int first[N*N],top;
int strong[N*N],dfn[N*N],low[N*N],stack[N*N],inst[N*N],con,idx,ts;
int id(int x,int y){
    return x*m+y;
}
struct ee{
    int y,w,next;
}e2[N*N*3];
int f2[N*N],top2,dis[N*N];
void init(){
    clc(first, -1);
    clc(dfn, -1);
    clc(inst, 0);
    clc(t,0);
    clc(strong, 0);
    clc(w,0);
    top = con = idx = ts = res = 0;
}
void add(int x,int y){
    e[top].y = y;
    e[top].next = first[x];
    first[x] = top++;
}
void tarjan(int x){
    dfn[x] = low[x] = ++idx;
    stack[ts++] = x;
    inst[x] = 1;
    for(int i = first[x];i!=-1;i=e[i].next){
        if(dfn[e[i].y] == -1){
            tarjan(e[i].y);
            low[x] = min(low[x],low[e[i].y]);
        }else if(inst[e[i].y])
            low[x] = min(low[x],dfn[e[i].y]);
    }
    if(dfn[x] == low[x]){
        con++;
        do{
            strong[stack[--ts]] = con;
            inst[stack[ts]] = 0;
            t[con] += w[stack[ts]];
        }while(x != stack[ts]);
    }
}
void add2(int x,int y,int w){
    e2[top2].y = y;
    e2[top2].w = w;
    e2[top2].next = f2[x];
    f2[x] = top2++;
}
int relax(int x,int y,int w){
    if(dis[x]+w > dis[y]){
        dis[y] = dis[x]+w;
        return 1;
    }
    return 0;
}
void spfa(int s){
    int i,now;
    int used[N*N]
    ;
    clc(dis,0);
    dis[s] = t[s];
    clc(used,0);
    used[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        now = q.front();
        q.pop();
        used[now] = 0;
        for(i = f2[now];i!=-1;i=e2[i].next)
            if(relax(now,e2[i].y,e2[i].w) && !used[e2[i].y]){
                used[e2[i].y] = 1;
                q.push(e2[i].y);
            }
    }
}
int main(){
    scanf("%d",&T);
    while (T--) {
        int i,j,a,b;
        init();
        scanf("%d %d",&n,&m);
        for(i = 0;i<n;i++)
            scanf("%s",s[i]);
        for(i = 0;i<n;i++)
            for(j = 0;j<m;j++){
                if(s[i][j] == '#')
                    continue;
                if(i+1<n && s[i+1][j] != '#')
                    add(id(i,j) , id(i+1,j));
                if(j+1<m && s[i][j+1] != '#')
                    add(id(i,j) , id(i,j+1));
                if(s[i][j] == '*'){
                    scanf("%d %d",&a,&b);
                    if(s[a][b] != '#')//WA点:要判断传送的终点位置是否可行
                        add(id(i,j),id(a,b));
                    
                }else
                    w[id(i,j)] = s[i][j]-'0';
            }
        tarjan(0);
        clc(f2,-1);
        top2 = 0;
        for(i = 0;i<n*m;i++){
            if(strong[i] == 0)
                continue;
            for(j = first[i];j!=-1;j=e[j].next)
                if(strong[e[j].y] && strong[i] != strong[e[j].y])
                    add2(strong[i],strong[e[j].y],t[strong[e[j].y]]);
        }
        spfa(strong[0]);
        for(i = 1;i<=con;i++)
            res = max(res,dis[i]);
        printf("%d\n",res);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值