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