状压DP<旅行商问题>

旅行社问题就是给一个有向图,从点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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值