1. 程式人生 > >PAT1018. Public Bike Management

PAT1018. Public Bike Management

從出發點到終點的路徑上的所有節點都要調整為cmax/2,但是隻能在去往終點的時候調整,回來的時候不調整,所以可能既要帶出又要帶回車子。這類題用dfs寫起來都很簡單,例如如下程式碼:

#include<algorithm>
#include<deque>
#include<cstdio>
using namespace std;
const int N=503,M=1<<30;
int dis[N][N],cmax,n,sp,m,has[N];
bool used[N];
int mlen=M,mout,mback,pre[N];
deque<int>path;
void dfs(int k,int len,int out,int back)
{
    if(k)
    {
        back+=has[k]-cmax/2;
        if(back<0)
        {
            out-=back;
            back=0;
        }
    }
    if(k==sp)
    {
        if((len<mlen)||(len==mlen)&&(out<mout||(out==mout&&back<mback)))
        {
            mlen=len,mout=out,mback=back;
            path.clear();
            path.push_back(k);
            while(k)   path.push_back(k=pre[k]);
        }
        return;
    }
    for(int i=1;i<=n;++i)
        if(!used[i]&&dis[k][i]!=M)
        {
            pre[i]=k;
            used[i]=true;
            dfs(i,len+dis[k][i],out,back);
            used[i]=false;
        }
}

int main()
{
    scanf("%d%d%d%d",&cmax,&n,&sp,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",has+i);
    fill_n(*dis,N*N,M);
    for(int i=0;i<m;++i)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        dis[a][b]=dis[b][a]=c;
    }
    used[0]=true;
    dfs(0,0,0,0);
    printf("%d %d",mout,path[path.size()-1]);
    for(int i=path.size()-2;i>=0;--i)
        printf("->%d",path[i]);
    printf(" %d\n",mback);
    return 0;
}

但dfs的效率比較低,所以最好用dijkstra演算法,如下面的程式碼,用dijkstra演算法求出圖中的所有單源最短路徑,前驅儲存在pre中,這樣對於任意一個節點x,可以找到原點到x的所有最短路徑,allpath函式就檢查了所有的從原點到目的地的最短路徑,對每條最短路徑用check函式檢視走這條路徑要帶出、帶回的車子數目,把最優路徑儲存在neo中。

#include<algorithm>
#include<bitset>
#include<iostream>
#include<vector>
using namespace std;
const int N=505,INF=1<<30;
int cur[N],cmax,n,src=0,dst,m;
//dijkstra只是求出所有的最短路徑來
int dis[N],adj[N][N];
vector<int>pre[N];
void dijkstra(){
  bitset<N>used;
  dis[src]=0;
  while(true){
    int mmin=INF,next=-1;
    for(int i=0;i<=n;++i)
      if(!used[i]&&dis[i]<mmin)
	mmin=dis[next=i];
    if(next==-1)break;
    used.set(next);

    for(int i=1;i<=n;++i)
      if(!used[i] && adj[next][i]!=INF){
	if(dis[i]>dis[next]+adj[next][i]){
	  dis[i]=dis[next]+adj[next][i];
	  pre[i].clear();
	  pre[i].push_back(next);
	}else if(dis[i]==dis[next]+adj[next][i]){
	  pre[i].push_back(next);
	}//else if..
      }//if(!used..
  }//while
}
//check 與 allpath一起工作,檢查終點為dst的所有最短路徑
int out=INF,in=INF;
vector<int>neo;
void check(vector<int>path){
  int _out=0,_in=0,mid=cmax/2;
  for(auto it=path.rbegin()+1;it!=path.rend();++it){
    int c=cur[*it];
    _in+=c-mid;
    if(_in<0)_out-=_in,_in=0;
  }
  if(_out<out || _out==out&&_in<in)
    {neo=path;out=_out;in=_in;}
}
void allpath(int k){
  static vector<int>path;
  path.push_back(k);
  if(k!=src)
    for(auto &x:pre[k])
      allpath(x);
  if(k==src) check(path);
  path.pop_back();
}

int main(){
  fill_n(dis,N,INF);
  fill_n(*adj,N*N,INF);
  cin>>cmax>>n>>dst>>m;
  for(int i=1;i<=n;++i)cin>>cur[i];
  for(int a,b,c,i=0;i<m;++i){
    cin>>a>>b>>c;
    adj[a][b]=adj[b][a]=c;
  }

  dijkstra();
  allpath(dst);

  cout<<out;
  for(auto it=neo.rbegin();it!=neo.rend();++it)
    cout<<(it==neo.rbegin()?" ":"->")<<*it;
  cout<<' '<<in<<endl;
}

不論用dfs還是用check檢查路徑,一個要點就是對於路徑上的每個節點,保持”到達該點需要帶出、帶回的車子數“這個性質,這是一個重要的不變式