# TIOJ 1028 . 旅遊規劃問題(位元DP)
### 題目連結:https://tioj.ck.tp.edu.tw/problems/1028
****
### 解題想法:
>DP[ i ][ j ],i 為目前位置的編號,j 為還沒走過的點集,因為數量很少,可以以[bit mask](https://codeforces.com/blog/entry/18169)表示,對於每一種狀態,用[Dijkstra](https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-using-priority_queue-stl/)求出每一個沒走過的點(不在這個狀態中)到這個狀態的最短距離,轉移式為
>
>DP[ i ][ j ] = 對於每個在 $j$ 中的 $x$ -> DP[ $x$ ][ $j$ ^ ($1$<<$x$) ] + ( i 到 x 的最短距離 ) 的最小值
### Dijkstra實作(每個狀態做一次):
``` cpp=
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define ll long long
#define F first
#define S second
#define INF (ll)1e17
struct Q{
int id;
ll dis;
bool operator<(const Q& rhs)const{
return dis>rhs.dis;
}
}; // 通過重載運算子,自訂 priority_queue 比較函式
vector<int> e[15]; // 每個點連到的點
ll d[15]; // 每個點到這個狀態的最短距離
int num[15][(1<<n)]; // 記錄最後要輸出的答案,即每個點在每個狀態中的下一個點是誰
bool done[15]; // 記錄每個點是否已經找到最短距離
fill(d,d+15,INF); // 初始為INF
memset(done,0,sizeof(done)); // 初始為未找到
priority_queue<Q> pq;
FOR(j,0,n)if(i&(1<<j)){
d[j]=dp[j][i^(1<<j)]; // 若已在此狀態中,將距離設為上一狀態
pq.push((Q){j,d[j]}); // 放入priority_queue
}
while(!pq.empty()){
auto now=pq.top(); pq.pop();
if(done[now.id]) continue; // 若已做過便不需再做一次
done[now.id]=1; // 標記為已做過
for(auto x:e[now.id])if(!(i&(1<<x.F))){ // 只取不在此狀態中的元素
if(d[x.F]>d[now.id]+x.S){ // 若距離較原本短,就更新
d[x.F]=d[now.id]+x.S;
pq.push((Q){x.F,d[x.F]});
num[x.F][i]=now.id;
}
else if(d[x.F]==d[now.id]+x.S&&num[x.F][i]>now.id){
// 若距離一樣但編號較小,也要更新,因為題目要求答案為字典序最小的
num[x.F][i]=now.id;
}
}
}
```
### 完整程式碼
``` cpp=
#include<bits/stdc++.h>
#define ll long long
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define INF (ll)1e17
#define F first
#define S second
using namespace std;
vector<pair<int,int> > e[15];
ll dp[15][1<<14],d[15];
int num[15][(1<<14)];
struct Q{
int id;
ll dis;
bool operator<(const Q& rhs) const{
return dis>rhs.dis;
}
};
priority_queue<Q> pq;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n,m;
cin>>n>>m;
FOR(i,0,m){
int a,b,c;
cin>>a>>b>>c;
e[a].push_back({b,c});
e[b].push_back({a,c});
}
int k,S=0,x,sta=-1;
bool done[15];
cin>>k;
FOR(i,0,k){
cin>>x;
S|=(1<<x);//making Bitmask
if(sta==-1) sta=x;//起點
}
FOR(i,0,n)FOR(j,0,n) dp[i][j]=INF;
FOR(i,0,n) dp[i][0]=0;//全部完成時
FOR(i,1,(1<<n))if(i==(i&S)){
while(!pq.empty()) pq.pop();
fill(d,d+n,INF);memset(done,0,sizeof(done));
FOR(j,0,n)if(i&(1<<j)){
d[j]=dp[j][i^(1<<j)];
pq.push((Q){j,d[j]});
}
while(!pq.empty()){
auto now=pq.top(); pq.pop();
if(done[now.id]) continue;
done[now.id]=1;
for(auto x:e[now.id])if(!(i&(1<<x.F))){
if(d[x.F]>d[now.id]+x.S){
d[x.F]=d[now.id]+x.S;
pq.push((Q){x.F,d[x.F]});
num[x.F][i]=now.id;
}
else if(d[x.F]==d[now.id]+x.S&&num[x.F][i]>now.id){
num[x.F][i]=now.id;
}
}
}
FOR(j,0,n)if(!(i&(1<<j))){
dp[j][i]=d[j];
}
}
cout<<"Minimum travel distance: "<<dp[sta][(S^(1<<sta))]<<endl;
int now=sta,cnt=0;
cout<<"Travel route: ";
while(1){
cnt++;
cout<<now<<" ";
if(S&(1<<now)) S=S^(1<<now);
now=num[now][S];
if(S==0) break;
}
}
```