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; }