POJ3592 Instantaneous Transference题解

来源:互联网 发布:找工作可靠的软件 编辑:程序博客网 时间:2024/06/07 06:59

题意:

  给一个矩形,矩形中某些点有一定数量的矿石,有些点为传送点,有些点为障碍。你驾驶采矿车(ore-miner truck,我也不知道是什么),从左上角出发,采尽量多的矿石,矿石不可再生。不能往左边或者上面走。传送点可以往左边或上面传。2<=n,m<=40

分析:

  可以把矩形看作一张图,每个格子为一个点,每个格子与它右边和下面的点连接一条有向边。每个传送点与它传送的位置连接一条有向边。如果右边或下面为#那么不连。值得注意的是题目并没有保证传送点传送到的一定不是#,所以需要进行判断。

   这样,我们得到了一个点数|V|=n*m,边数最大|E|=O(n*m)的有向有环图。答案就是限制只能取一次的最长路。

  我们现在的任务就是把只能取一次抽象成另一种能实现的东西,不然暴力是指数级的。

  很显然,向后传送不需要考虑,只考虑传回前面,在图上的表现为成环,或者说同属一个强连通分量。

  很自然地就可以发现同属一个强连通分量的点都可以同时取到,我们考虑用tarjan缩点,这样建的就是一个DAG,在这个DAG上跑最长路,就是答案。

   这里值得一提的是,这里不能用dijkstra,同时我们可以下结论:求最长路时,dijkstra算法只适用于负权图,求最短路时,dijkstra算法只适用于正权图。所以这里要写SPFA。

代码:用emacs写的,所以不要吐槽两格缩进

 

  1 #include<iostream>  2 #include<cstdio>  3 #include<cstdlib>  4 #include<vector>  5 #include<cstring>  6 #include<stack>  7 #include<queue>  8 #include<algorithm>  9 using namespace std; 10 int low[1650],dfn[1650],arr[1650],scc[1650],al,cl,w[1650]; 11 vector<int> g[1650]; 12 vector<int> ng[1650]; 13 stack <int> sta; 14 void tarjan(int now){ 15   low[now]=dfn[now]=++cl; 16   sta.push(now); 17   for(int i=0;i<g[now].size();i++){ 18     int k=g[now][i]; 19     if(arr[k])continue; 20     if(!dfn[k]){ 21       tarjan(k); 22       low[now]=min(low[now],low[k]); 23     }else 24       low[now]=min(low[now],dfn[k]); 25   } 26   if(low[now]==dfn[now]){ 27     al++; 28     while(1){ 29       int u=sta.top(); 30       sta.pop(); 31       arr[u]=1; 32       scc[u]=al; 33       if(u==now) break; 34     } 35   } 36 } 37 int a[50][50]; 38 void readint(int n,int m){ 39   memset(arr,0,sizeof(arr)); 40   memset(w,0,sizeof(w)); 41   memset(dfn,0,sizeof(dfn)); 42   memset(low,0,sizeof(low)); 43   memset(scc,0,sizeof(scc)); 44   al=cl=0; 45   for(int i=1;i<=n*m;i++)g[i].clear(),ng[i].clear(); 46   for(int i=1;i<=n;i++) 47     for(int j=1;j<=m;j++){ 48       char x;cin>>x; 49       if(x=='*')a[i][j]=10; 50       else 51     if(x=='#')a[i][j]=-1; 52         else a[i][j]=(int)(x-48); 53     } 54   for(int i=1;i<=n;i++){ 55     for(int j=1;j<=m;j++){ 56       if(j+1<=m&&a[i][j+1]!=-1)g[i*m-m+j].push_back(i*m-m+j+1); 57       if(i+1<=n&&a[i+1][j]!=-1)g[i*m-m+j].push_back(i*m+j); 58       if(a[i][j]==10){ 59     int x,y;cin>>x>>y; 60     if(a[x+1][y+1]==-1)continue; 61     else g[i*m-m+j].push_back(x*m+y+1); 62       } 63     } 64   } 65 } 66 void dij(int n){//SPFA找最长路 67   queue <int> que; 68   int dist[2000]; 69   memset(dist,0,sizeof(dist)); 70   dist[scc[1]]=w[scc[1]]; 71   que.push(scc[1]); 72   while(!que.empty()){ 73     int k=que.front(); 74     for(int i=0;i<ng[k].size();i++){ 75       if(dist[k]+w[ng[k][i]]>dist[ng[k][i]]){ 76     dist[ng[k][i]]=dist[k]+w[ng[k][i]]; 77     que.push(ng[k][i]); 78       } 79     } 80     que.pop(); 81   } 82   int maxx=0; 83   for(int i=1;i<=n;i++)maxx=max(maxx,dist[i]); 84   cout<<maxx<<endl; 85 } 86  87 int main(){ 88   int t;cin >> t; 89     while(t--){ 90     int n,m; 91     cin>>n>>m; 92     readint(n,m); 93     for(int i=1;i<=n*m;i++) 94       if(!arr[i]) 95     tarjan(i);//求强连通分量 96     for(int i=1;i<=n*m;i++){ 97       for(int j=0;j<g[i].size();j++){ 98     if(scc[i]==scc[g[i][j]])continue; 99     ng[scc[i]].push_back(scc[g[i][j]]);100       }101     }//缩点102     for(int i=1;i<=n;i++){103       for(int j=1;j<=m;j++){104     if(a[i][j]==-1||a[i][j]==10)continue;105     w[scc[i*m-m+j]]+=a[i][j];106       }107     }//算新的点权108     dij(al);//求最长路109   }110   return 0;
111 }

 

 

 

 

 

 

  

 

0 0
原创粉丝点击