codevs售货员的难题 —— 状态压缩动态规划[四星]

来源:互联网 发布:米表源码 编辑:程序博客网 时间:2024/04/26 12:28

题目链接:戳我~

题目描述 Description

某乡有n个村庄,有一个售货员,他要到各个村庄去售货,各村庄之间的路程s是已知的,且A村到B村与B村到A村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

输入描述 Input Description

村庄数n和各村之间的路程(均是整数)

输出描述 Output Description

最短的路程

样例输入 Sample Input

3

0 2 1

1 0 2

2 1 0

样例输出 Sample Output

3

第一次打状压DP,以前只是听说不敢打…… 状压的思想核心还是比较易懂的
核心:
把状态压缩为一个二进制序列,用十进制保存,二进制中每一位都代表着不同的意义,比如说10,它的二进制是1010,可以理解为4号位和2号位的目标达成,1号和3号的目标没有达成。

压缩状态的目的是省空间,因为如果每一种状态开一个多维数组的话太占空间,并且转移也不好写,状压后转移写起来很方便,因为是二进制,主要通过位运算来实现它……


回到题目,看到n <= 15想到状压可以做,状压用在这里就是最外层从1for到2^n,这样就达到了枚举所有状态的目的,(具体实现详见代码),然后核心DP转移方程如下

dp[j][((1 << j-1) | i)] = min(dp[j][((1 << j-1) | i)],dp[k][i] + map[k][j]);

dp[i][j]表示的是从起点到i号点在j状态下的最短路径。
上面式子有一个条件,j没有走过而k走过(看不懂的话,下面有完整代码),那么上面式子的意思就是:从起点到j,状态从原来的i二进制第j-1项为0转移到为1时,取它本身和从k点状态i(即还没有走到j这个村子,因为此时i中对应的j的状态点为0),加上k到j的距离,这里要仔细想想。(位运算的优先级很低,(1 << j-1)优先算减法,然后再算<<,<<的意思是将原来的数字再二进制中左移多少位,1 << j-1位的含义是2^(j-1),2的j-1次方)。

最后时进行这一步:

for(int i = 2;i <= n;i ++){    ans = min(ans,dp[i][(1<<n)-1] + map[i][1]);}

这一步ans初值是INF,然后dp数组表示的是从起点到i点并且除了起点所有的村庄都走的最小值,然后再加上从i点到起点的距离,再所有情况中取最小值就是答案~


下面附上完整代码

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<deque>#include<algorithm>#include<cmath>using namespace std;const int size = 124500;int dp[23][size];int map[23][23];int main(){    int n;    scanf("%d",&n);    for(int i = 1;i <= n;i ++)        for(int j = 1;j <= n;j ++)            scanf("%d",&map[i][j]);    memset(dp,63,sizeof(dp));    dp[1][1] = 0;    for(int i = 0;i <= (1 << n);i ++)    {        for(int j = 1;j <= n;j ++)        {            if(((1 << j-1) & i) == 0)            {                for(int k = 1;k <= n;k ++)                {                    if(((1 << k-1) & i))                    {                        dp[j][((1 << j-1) | i)] = min(dp[j][((1 << j-1) | i)],dp[k][i] + map[k][j]);                    }                }            }        }    }    int ans = 2147483640;    for(int i = 2;i <= n;i ++)    {        ans = min(ans,dp[i][(1<<n)-1] + map[i][1]);    }    printf("%d",ans);    return 0;}
2 0
原创粉丝点击