题解_售货员的难题(状压DP+优化)

3 篇文章 0 订阅
3 篇文章 0 订阅

售货员的难题

题目描述

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

【输入】

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

【输出】

最短的路程。

【样例】
Input
3			 {村庄数}
0  2  1      {村庄1到各村的路程}
1  0  2      {村庄2到各村的路程}
2  1  0      {村庄3到各村的路程}
Output
3

讲解

首先,此题和最短Hamilton路径类似,状态设计几乎一样。

状态转移方程

F [ j ,   (   ( 1 < < ( j − 1 )   ) ∣ i ) ] = m i n ( F [ j ,   ( ( 1 < < ( j − 1 ) ) ∣ i ) ] , F [ k , i ] + w [ k , j ] ) F[j,~(~\color{green}{(1 << (j-1)~)}\color{black} | i)] = min(F[j,~(\color{green}{(1 << (j-1)) }\color{black}| i)],F[k,i] + w[k,j]) F[j, ( (1<<(j1) )i)]=min(F[j, ((1<<(j1))i)],F[k,i]+w[k,j])

解释:

i < < ( j − 1 ) i<<(j-1) i<<(j1)即作第j个村庄的状态形式, ∣ i |i i(即 o r   i or~i or i)意思是,如果当前状态没走过,则走;走过了,则不重复走,防止进位改变本意。

注意

因为1节点一定会走,所以我们将它当做0号节点,这样可以节省一半的时间和空间。

代码

#include <iostream>
#include <cstdio>
#include <cstring> 

using namespace std;

int n, w[20][20], ans = 0x3f3f3f3f;
int dp[1 << 19][20];

int main()
{
    scanf("%d", &n);
    memset(dp, 0x3f, sizeof(dp));
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j)
            scanf("%d", &w[i][j]);
    for(int i = 2; i <= n; ++i)
        dp[1 << (i - 2)][i - 1] = w[0][i - 1];
    int len = 1 << (n - 1);
    for(int i = 1; i < len; ++i)
        for(int j = 1; j < n; ++j)
            if(!(1 << (j - 1) & i))
                for(int k = 1; k < n; ++k)
                    if(1 << (k - 1) & i)
                        dp[1 << (j - 1) | i][j] = min(dp[1 << (j - 1) | i][j], dp[i][k] + w[k][j]);
    for (int i = 1; i < n; ++i)
        ans = min(ans, dp[len - 1][i] + w[i][0]);
    printf("%d", ans);
    return 0;
}

最后考大家一道“编程”题:
我们能且只能将一个变量名修改为另一个变量名,在无法打字、选中的情况下,至少需要修改几次才能将下列代码的循环变量顺序修改为i, j, k?
欢迎评论、留言!

for(int k = 1; k <= n; ++k)
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			cin >> num[k][i][j];

完结,撒花

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值