POJ 3592 Instantaneous Transference(强联通分量 Tarjan)
http://poj.org/problem?id=3592
题意 :给你一个n*m的矩阵,每个位置上都有一个字符,如果是数字代表这个地方有该数量的金矿,如果是*代表这个地方有传送带并且没有金矿,可以传送到指定的位置,如果是#代表该位置不可走,初始位置在左上角,只能向下或向右走,并且走到传送带的时候可选择是否传送。问当走出去的时候能获得的最大近况数是多少。
思路 :先将二维矩阵转化成一维的点建图,可以向下向右建图,而且传送带也可以建边,缩点之后再建边,最后用spfa求最长路。
#include <iostream> #include <stdio.h> #include <queue> #include <string.h> using namespace std; const int maxn = 555555 ; const int INF = -1000000000 ; int belong[maxn],instack[maxn],dfn[maxn],low[maxn],g[maxn],map[maxn],c[maxn] ; //belong[i]指的是i点所在的联通分量的编号。instack模拟的是栈,因为每个节点是要入栈的,来判断是否属于同一个分量 //dfn[i]为搜索i结点的次序编号,Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。 //g[i]是指将一个矩阵从0到n*m编号之后的,i点所在的那个位置的宝藏数,map[i]记录的是第i个传送带的位置 //c[i]记录的是第i个联通分量中宝藏的总数。 int cntt,cnt,top,bcc_clock,cntb,n,m,num ; int head1[maxn] ,head[maxn]; char point[100][100] ;//输入的地图 int dis[maxn],coun[maxn],index ; bool vis[maxn],flag[maxn] ; int dire[2][2] = {{0,1},{1,0}} ; struct node { int u,v,w,next ; } p[maxn] ,ch[maxn]; void addedge(int u,int v) { p[cnt].u = u ; p[cnt].v = v ; p[cnt].next = head[u] ; head[u] = cnt++ ; } void addnode(int u,int v,int w) { ch[cntt].u = u ; ch[cntt].v = v ; ch[cntt].w = w ; ch[cntt].next = head1[u] ; head1[u] = cntt++ ; } void tarjan(int u) { vis[u] = true ; dfn[u] = low[u] = ++bcc_clock ; instack[++top] = u ; for(int i = head[u] ; i+1 ; i = p[i].next) { int v = p[i].v ; if(!dfn[v]) { tarjan(v) ; low[u] = min(low[u],low[v]) ; } else if(vis[v]) low[u] = min(low[u],dfn[v]) ; } if(dfn[u] == low[u]) { cntb++ ; int v ; do { v = instack[top--] ; vis[v] = false ; belong[v] = cntb ; } while(v != u) ; } } void Init() { memset(dfn,0,sizeof(dfn)) ; memset(low,0,sizeof(low)) ; memset(belong,0,sizeof(belong)) ; memset(c,0,sizeof(c)) ; memset(vis,0,sizeof(vis)) ; num = 0 ; memset(head,-1,sizeof(head)) ; memset(head1,-1,sizeof(head1)) ; cnt = 0,top = 0 ,cntb = 0,bcc_clock = 0,cntt = 0 ; } bool relax(int u,int v,int w) { if(dis[v] < dis[u] + w) { dis[v] = dis[u] + w ; return true ; } return false ; } bool spfa(int u) { memset(flag,false,sizeof(flag)) ; memset(coun,0,sizeof(coun)) ; flag[u] = true ; for(int i = 0 ; i <= cntb ; i++) dis[i] = INF ; queue<int >Q ; Q.push(u) ; dis[u] = 0 ; while(!Q.empty ()) { int st = Q.front() ; Q.pop() ; flag[st] = false ; for(int i = head1[st] ; i+1 ; i = ch[i].next) { if(relax(st,ch[i].v,ch[i].w) && !flag[ch[i].v]) { if((++coun[ch[i].v]) > m*n) return false ; Q.push(ch[i].v) ; flag[ch[i].v] = true ; } } } index = 0 ; for(int i = 1 ; i <= cntb ; i++) index = max(index,dis[i]) ; return true ; } int main() { int T ; scanf("%d",&T) ; while(T--) { scanf("%d %d",&n,&m) ; int cnnt = 0;//记录传送带的个数 Init() ; getchar() ; for(int i = 0 ; i < n ; i++) scanf("%s",point[i]) ; for(int i = 0 ; i < n ; i++) { for(int j = 0 ; j < m ; j++) { int k = i*m+j ; if(point[i][j] == ‘#‘) { g[k] = -1 ; continue ; } else { if(point[i][j] == ‘*‘) { map[cnnt++] = k ; g[k] = 0 ; } else if(point[i][j] >= ‘0‘ && point[i][j] <= ‘9‘) g[k] = point[i][j] - ‘0‘ ; for(int ii = 0 ; ii < 2 ; ii++) { int xx = i+dire[ii][0] ; int yy = j+dire[ii][1] ; if(xx < n && yy < m) { if(point[xx][yy] != ‘#‘) addedge(k,xx*m+yy) ; } } } } } for(int i = 0 ; i < cnnt ; i++) { int x,y ; scanf("%d %d",&x,&y) ; if(point[x][y] != ‘#‘) addedge(map[i],x*m+y) ; } // Init() ; for(int i = 0 ; i < n*m ; i++) if(!dfn[i]) tarjan(i) ; for(int i = 0 ; i < n*m ; i++) c[belong[i]] += g[i] ; addnode(0,belong[0],c[belong[0]]);//缩点之后的建边 for(int i = 0 ; i < n*m ; i++) { for(int j = head[i] ; j + 1 ; j = p[j].next) { int v = p[j].v ; if(belong[i] != belong[v]) { addnode(belong[i],belong[v],c[belong[v]]) ;//两个点不属于同一个联通分量 } } } spfa(0) ;//求最长路 printf("%d\n",index) ; } return 0; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。