状态压缩DP与TSP问题

状态压缩DP

DP过程中的状态不可能像背包问题一样只有整数,肯定有各种各样稀奇古怪的状态,需要不止一个变量来表示。这种情况下如果需要使用DP 就必须把状态压缩成一个数来表示,并且一个数只能对应于一种状态。

特别地,对于集合我们可以把每一个元素的选取与否对应到一个二进制位里,从而把状态压缩成一个整数,大大方便了计算和维护。

对于不是整数的情况,很多时候很难确定一个合适的递推顺序,因此使用记忆化搜索可以避免这个问题。如下面TSP问题的法一。

TSP问题

一张图上有n个点,给定相应的邻接矩阵,需要求出从0号节点出发,经过且只经过每个顶点一次,最后仍回到0号节点的最小边权。TSP问题可以用状压DP来快速求解。

定义dp[S][v]为已经经过了点集S之后,目前在点v(v已经包含在S中),回到0节点的最小边权。

所以有如下的递推公式



**dp[V[0] = 0

dp[S][v] = min(dp[S∪{u}][u]+d[v][u])(其中u不属于S)**



将S看作一个长度为n的bit流,第几号节点访问过就把S的第几号节点置为1,其他都是0,这样就可以将状态压缩成了一个数字来表示,并且有一一对应性。

采用记忆化搜索的TSP状压DP代码如下

int n;
int d[maxn][maxn];
int dp[1<<maxn][maxn];
int rec(int S,int v)
{
    if(dp[S][v] >= 0) {
        return dp[S][v];
    }
    if(v == 0 && S == (1<<maxn)-1) return dp[S][v] = 0;
    int ans = INF;
    for(int i = 0 ; i < n ; i ++) {
        if(!(S>>i&1)) {
            ans = min(ans,d[v][i]+rec(S|(1<<i),i));
        }
    }
    return dp[S][v] = ans;
}
void solve(void)
{
    memset(dp,-1,sizoef(dp));
    cout << rec(0,0) << endl;
}

此外也可以不用记忆化搜索,观察递推的顺序从而使用循环求解。

发现对于任意的两个整数i和j 如果它们对应的集合满足S(i)包含于S(j) 那么i<=j。代码如下

int dp[1<<maxn][maxn];
void solve()
{
    for(int i = 0 ; i < (1<<n) ; i ++) {
        fill(dp[S],dp[S]+n,INF);
    }
    dp[(1<<n)-1][0] = 0;
    for(int S = (1<<n)-1 ; S >= 0 ; S --) {
        for(int i = 0 ; i < n ; i ++) {
            for(int j = 0 ; j < n ; j ++) {
                if(!(S>>j&1))
                    dp[S][i] = min(dp[S][i],dp[S|1<<j][j]+d[i][j]);
            }
        }
    }
    cout << dp[0][0] << endl;
}
时间: 2024-10-23 00:51:44

状态压缩DP与TSP问题的相关文章

poj 2688 状态压缩dp解tsp

题意: 裸的tsp. 分析: 用bfs求出任意两点之间的距离后可以暴搜也可以用next_permutation水,但效率肯定不如状压dp.dp[s][u]表示从0出发访问过s集合中的点,目前在点u走过的最短路程. 代码: //poj 2688 //sep9 #include <iostream> #include <queue> using namespace std; const int maxW=32; const int maxN=12; int dx[4]={-1,1,0,

poj 3311 Hie with the Pie(状态压缩dp)

Description The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortunately, due to cutbacks, they can afford to hire only one driver to do the deliveries. He will wait for 1 or more (up to 10) orders to be

POJ 3311 Hie with the Pie(Floyd+状态压缩DP)

贴一个TSP讲解:点击打开链接 错误的转移方程 dp[i][j] 把i当作了步数,以为至多走N步就可以了.作死啊 #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define maxn 1100 #define inf 0x3f3f3f3f const double eps=1e-8; using namespace std; int dp[12][1<

学习笔记:状态压缩DP

我们知道,用DP解决一个问题的时候很重要的一环就是状态的表示,一般来说,一个数组即可保存状态.但是有这样的一些题 目,它们具有DP问题的特性,但是状态中所包含的信息过多,如果要用数组来保存状态的话需要四维以上的数组.于是,我们就需要通过状态压缩来保存状态,而 使用状态压缩来保存状态的DP就叫做状态压缩DP. 一道例题: HOJ 2662 有一个n*m的棋盘(n.m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻(每个棋子最多和周围4个棋子相邻).求合法的方案总数.

Light OJ 1316 A Wedding Party 最短路+状态压缩DP

题目来源:Light OJ 1316 1316 - A Wedding Party 题意:和HDU 4284 差不多 有一些商店 从起点到终点在走过尽量多商店的情况下求最短路 思路:首先预处理每两点之前的最短路 然后只考虑那些商店 个数小于15嘛 就是TSP问题 状态压缩DP搞一下 状态压缩姿势不对 有必要加强 #include <cstdio> #include <algorithm> #include <queue> #include <vector>

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

HDU1565(状态压缩dp)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8170    Accepted Submission(s): 3095 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数

HDU 3001【状态压缩DP】

题意: 给n个点m条无向边. 要求每个点最多走两次,要访问所有的点给出要求路线中边的权值总和最小. 思路: 三进制状态压缩DP,0代表走了0次,1,2类推. 第一次弄三进制状态压缩DP,感觉重点是对数据的预处理,利用数组分解各个位数,从而达到类似二进制的目的. 然后就是状态的表示,dp[s][i]表示状态s时到达i的最优值. 状态转移也一目了然,不废话. #include<stdio.h> #include<string.h> #include<algorithm> u

Victor and World(spfa+状态压缩dp)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5418 Victor and World Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others)Total Submission(s): 958    Accepted Submission(s): 431 Problem Description After trying hard fo