售货员的难题

售货员的难题

Description

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

Input

第一行为一个整数n(n<=40),表示城市的总个数。
接下来是一个n*n的矩阵,用来表示城市间的连通情况以及花费,例如path[i][j]=len,len=0表示从城市i到城市j没有通路,len>0表示从i到j的路程长度为len。

Output

输出一行,对于给定的问题,如果找到了最小路程(花费),输出该最小花费,如果没有通路可以到达每个城市,则输出-1。

Sample Input

3
0 2 1
1 0 2
2 1 0

Sample Output

3

Hint

样例提示:
3 {村庄数}
0 2 1 {村庄1到各村的路程}
1 0 2 {村庄2到各村的路程}
2 1 0 {村庄3到各村的路程}

Source

搜索, Np问题, 状压DP, 模拟退火

Solution

这道题需要用到状态压缩

用一个二进制数表示当前状态下访问每一个村庄的状态(不需考虑访问村庄的顺序,只用考虑状态即可),用0表示没有访问,1表示已经访问过

设置一个二维数组dp[i][j],其中i为二进制表示的村庄访问状态的十进制形式,j表示当前所在的村庄,即下一次访问需要从第j个村庄开始

对于每一种状态,存在转移方程dp[i + 1 << j][j] = min(dp[i + 1 << j][j], dp[i][j] + d[j][v]),当然前提是i状态下的j访问过,v没有访问过

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define L 50
#define INF 1000000009
#define get(i, j) (i / len[j - 1]) % 2
using namespace std;

inline int gi() {
  char cj = getchar();
  int ans = 0, f = 1;
  while (cj < '0' || cj > '9') {
    if (cj == '-') f = -1;cj = getchar();
  }
  while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
  return f * ans;
}

int n, len[L], d[L][L], dp[(1 << 16)][L], ans = INF;

int main() {
  len[0] = 1;
  for (int i = 1; i <= 20; ++i) len[i] = len[i - 1] * 2;
  memset(dp, INF, sizeof(dp));
  dp[1][1] = 0;
  n = gi();
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j)
      d[i][j] = gi();
  for (int i = 0; i <= len[n]; ++i) {
    for (int j = 1; j <= n; ++j) {
      if (get(i, j) == 0) continue;
      for (int v = 1; v <= n; ++v) {
	if (get(i, v)) continue;
	dp[i + len[v - 1]][v] = min(dp[i + len[v - 1]][v], dp[i][j] + d[j][v]);
      }
    }
  }
  for (int j = 2; j <= n; ++j) ans = min(ans, dp[len[n] - 1][j] + d[j][1]);
  if (ans >= INF) ans = -1;
  printf("%d\n", ans);
  return 0;
}

Summary

第一次提交的时候被数据范围骗了,空间开爆了

第二次提交的时候发现初始化除了问题:不用for (int i = 1; i <= 20; ++i) dp[len[i - 1]][i] = 0,更新dp数组,因为这句话的意思是考虑每一个村庄作为初始村庄的最小费用,而此题要求从1号村庄开始

另外不要忘记了最后还要回到1号村庄

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值