旅行社问题就是给一个有向图,从点0走一圈全部结点再走回来所花费的费用最少是多少。
状压DP一般n<=15。
因为是走完全部点,那么走到2点的费用有可能走了5,也可能没走,那么就不能简单的用最短路来找。
因为要求跑完全部点回到0,而最小树形图的话无法做到。
状压dp是指在一个点上存在多种状态,这些状态可以用二进制来进行表示。
因为是最后走到0的费用,那么可以有这么个公式dp[S][I]=min(dp[S][i],dp[S|1<<j]+map[i][j]。(前提是S中没走过j这个点)
S表示走过的点集合(注意从0开始走,但0这个点没有走过(二进制中其是末尾的1),所以最后走回0)
也就是走完这个集合且目前在v的最小费用,是走完
for(j=0;j<n;j++)
dp[S][I]=min(dp[S][i],dp[S|1<<j]+map[i][j]
相当于是从某个顶点退回来的最小费用,也就是这个dp是从终点逆着dp的。
那么只要初始化dp[1<<n-1][0]=0。
第一次循环就可以得到走完各个点差一步到0的最小费用,然后再向后dp,直到DP[0][0]
相当dfs的做法:
#include <queue>
#include<iostream>
#include<stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <ctime>
using namespace std;
struct ttt{
double x1,y1,x2,y2;
};
int dp[1<<17][16];
int map1[16][16];
int n;
int dfs(int S,int v){
if(dp[S][v]>=0)
return dp[S][v];
if(S==(1<<n)-1&&v==0){
dp[S][v]=0;
return 0;
}
int res=1e9+7;
for(int i=0;i<n;i++)
if(!(S>>i&1)){
res=min(res,dfs(S|1<<i,i)+map1[v][i]);
}
dp[S][v]=res;
return res;
}
int main(){
freopen("in.txt","r",stdin);
//freopen("output.txt","w",stdout);
int i,j,k,l,f1,f3,t1,t2,t3,m;
int l1,l2,l3,f4,f5;
int sum1;
memset(dp,-1,sizeof(dp)); //之所以可以为-1是因为走到终点后把费用变成0
cin >> n >> m;
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
map1[i][j]=1e9+7;//对图的边记得初始化为无穷和判重
for(i=1;i<=m;i++){
cin >> t1>> t2>> t3;
map1[t1][t2]=t3;
}
cout << dfs(0,0) << endl;
return 0;
}
初始的时候,需要设置所有初始dp都是无穷大的
#include <queue>
#include<iostream>
#include<stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <ctime>
using namespace std;
struct ttt{
double x1,y1,x2,y2;
};
int dp[1<<17][16];
int map1[16][16];
int n;
int main(){
freopen("in.txt","r",stdin);
//freopen("output.txt","w",stdout);
int i,j,k,l,f1,f3,t1,t2,t3,m;
int l1,l2,l3,f4,f5;
int sum1;
memset(dp,-1,sizeof(dp)); //之所以可以为-1是因为走到终点后把费用变成0
cin >> n >> m;
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
map1[i][j]=1e9+7;//对图的边记得初始化为无穷和判重
for(i=1;i<=m;i++){
cin >> t1>> t2>> t3;
map1[t1][t2]=t3;
}
int S;
for(S=(1<<n)-1;S>=0;S--)
for(i=0;i<n;i++)
dp[S][i]=1e9+7; //但是其他也要为无穷大啊
dp[(1<<n)-1][0]=0; //因为每个点是作为起点的费用,所以只赋值一个终点即可
for(S=(1<<n)-2;S>=0;S--)
for(i=0;i<n;i++){ //但是其他也要为无穷大啊
for(j=0;j<n;j++)
if((S>>j&1)==0) //S中无j
dp[S][i]=min(dp[S][i],dp[S|1<<j][j]+map1[i][j]);
}
cout << dp[0][0] <<endl;
return 0;
}