【3阶动规+状态压缩】s到达G



S对应point[0],G对应Point[n-1].中间点对应1~n-2

针对每个中间state有 

        len[state][s]+dist[s][t]  >=  len[state|(1<<(t-1))][t],state=1 to 1<<(n-2)-1

        其中len[state][s]<无穷(点s可以且已经被state最后访问),dist[s][t]<无穷(点s可以到达t),且state&(1<<(t-1))==0 (即点t尚未被状态state访问,从而state可达t)



#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
typedef pair<int,int> Pair;
#define INF 0x7ffffff
#define MAXSIZE 10000
class Orienteering{
  public:
    void main();
  private:
    bool init(char buf[],int &w,int &h,vector<Pair> &points);
    void bfs(char buf[],int w,int h,const vector<Pair> &points,vector<vector<int> > &dist);
    int dp(const vector<vector<int> > &dist);
}; 

/**
 * calculate min-cost of all (i,j), point i and j in points[]
 *  and store in dist[i][j]
 * params: @buf[]: the map
 *  @w: the width  @h: the height, 0<w,h<=100
 *  @points[]: the point array,
 *  @dist[][]: elements init as INF
 * output: @dist[][]
 */
void Orienteering::bfs(char buf[],int w,int h,const vector<Pair> &points,vector<vector<int> > &dist){
  int n=points.size();

  // record points' number on pNum[][]
  int *pNum=new int[w*h];
  memset(pNum,-1,w*h*sizeof(int));
  for(int i=0;i<n;i++){
    const Pair &p=points[i];
    pNum[p.first*w+p.second]=i;
  }

  // minDst[i][j] is the min-distance between point (i,j)
  int * minDst=new int[w*h];
  // four directions
  int drt[4][2]={{1,0},{-1,0},{0,1},{0,-1}};

  // find nearest distances from point[i] to other points
  for(int i=0;i<n;i++){
    const Pair &p=points[i];
    queue<Pair> que;
    que.push(p);
    int cnt=1;

    memset(minDst,-1,w*h*sizeof(int));
    minDst[p.first*w+p.second]=0;

    while(!que.empty()){
      Pair p=que.front(); que.pop();
      int x0=p.first,y0=p.second;

      // visit direction: left/right/up/down
      for(int j=0;j<4;j++){
        int x=x0+drt[j][0],y=y0+drt[j][1];

        // ignore positions which is visited or can't be visited
        if(x<0 || y<0 || x>=h || y>=w || buf[x*w+y]=='#'|| minDst[x*w+y]>=0) 
          continue;

        minDst[x*w+y]=minDst[x0*w+y0]+1;
        que.push(Pair(x,y));

        int k=pNum[x*w+y];
        if(k>=0){// if readched a point
          dist[i][k]=minDst[x*w+y];
          // if(k<i) assert(dist[k][i]==dist[i][k]);
          cnt++;
          if(cnt==n) break;
        }
      }
    }
  }
  delete[] minDst;
  delete[] pNum;
}


/**
 * find the min-cost from point 0 to point n-1 with all other points visited
 * params: @dist[i][j]: min-distance from point i to j
 * return: the min-distance from point 0 to n-1 with all check-points passed
 *    INF if not reachable or input don't satisfy specifications
 */
int Orienteering::dp(const vector<vector<int> > &dist){
  int n=dist.size();
  int lp=0,rp=n-1;
  if(n<2||n>sizeof(int)*8) return INF;

  int size=1<<(n-2), lst=size-1;// lst is the state that all check-points(1~n-2) has been visited
  // len[i][s] stands for current visit state is i(compressed binary number for check-points 1~n-2), with point s last visited 
  vector<vector<int> > len(size,vector<int>(n-1,INF));
  vector<vector<int> > path(size,vector<int>(n-1,INF));
  
  len[0][lp]=0;// init: in state 0, with point 0(start-point 'S') last visited
  for(int i=0;i<=lst;i++){
    for(int s=lp;s<rp;s++){
      if(len[i][s]<INF){
        for(int t=lp+1,mask=1;t<rp;t++,mask<<=1){
          if(!(i&mask) && dist[s][t]<INF){// try to go from <state i,point s> to <state i|mask,point t>
            int tmp=len[i|mask][t];
            len[i|mask][t]=min(len[i|mask][t],len[i][s]+dist[s][t]);
            if(len[i|mask][t] < tmp)
              path[i|mask][t]=s;// record the path 
          }
        }
      }
    }
  }

  // go to end-point rp from <state lst, point s>
  int minLen=INF;
  int l=INF;
  for(int s=lp;s<rp;s++)
    if(len[lst][s]<INF && dist[s][rp]<INF){
      int tmp=minLen;
      minLen=std::min(minLen,len[lst][s]+dist[s][rp]);
      if(minLen<tmp){
        l=s;
      }
    }

  // print min-path
  int state=lst,point=l;
  cout<<"print path:"<<endl;
  cout<<rp<<" <- ";
  while(point<INF){
    cout<<point;
    if(point==0) break;
    cout<<" <- ";
    int tmp=path[state][point];
    state-=(1<<(point-1));
    point=tmp;
  }
  cout<<endl;

  return minLen; 
}


/**
 * init the points and buffer
 * record map in buf[], check points and S/G in points[]
 * @points[]: points[0] as point 'S', 1~n-2 as check-points, n-1 as 'G'
 * @return: true if parameters satisfy conditions
 */
bool Orienteering::init(char buf[],int &w,int &h,vector<Pair> &points){
  scanf("%d %d\n",&w,&h);
  // if w or h don't satisfy conditions
  if( w<1 || h<1 || w*h>MAXSIZE ) return false;

  Pair tmp(-1,-1),start=tmp,end=tmp;
  points.clear();
  points.push_back(start);

  for(int i=0;i<h;i++){
    scanf("%s\n",buf+i*w); 
    for(int j=0;j<w;j++){
      char c=buf[i*w+j];
      if(c=='@') points.push_back(Pair(i,j));
      else if(c=='S') {
        if(start!=tmp) return -1; // S duplicated
        start=Pair(i,j);
      }
      else if(c=='G') {
        if(end!=tmp) return -1; // G duplicated
        end=Pair(i,j);
      }
    }
  }
  // if no 'S' or 'G' found
  if(start==tmp||end==tmp) return false;
  points[0]=start;
  points.push_back(end);
  if(points.size()>20) return false;
  return true;
}

/**
 *
 */
void Orienteering::main(){
  // record points that must be passed
  int w,h;
  char buf[10010]; 
  vector<Pair> points;

  if(!Orienteering::init(buf,w,h,points)) {
    printf("-1\n");
    return;
  };

  // find the nearest distance  between points in points[]
  int n=points.size(); 
  vector<vector<int> > dist(n,vector<int>(n,INF));
  Orienteering::bfs(buf,w,h,points,dist);

  // find the nearest path from start-point to end-point passing all points
  int dst=Orienteering::dp(dist);

  if(dst==INF) printf("-1\n");
  else printf("%d\n",dst);
}


int main(int argc,char *argv[]){
  Orienteering o;
  o.main();
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值