POJ3592 Instantaneous Transference题解

题意:

  给一个矩形,矩形中某些点有一定数量的矿石,有些点为传送点,有些点为障碍。你驾驶采矿车(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 }

 

 

 

 

 

 

  

 

posted @ 2017-01-26 14:00  社会主义市场经济  阅读(213)  评论(0编辑  收藏  举报