poj3311 经典tsp问题

题目的大概意思就是一个人到一些城市送披萨,要求找到一条路径能够遍历每一个城市后返回出发点,并且路径距离最短。最后输出最短距离即可。注意:每一个城市可重复访问多次。

由于题中明确说了两个城市间的直接可达路径(即不经过其它城市结点)不一定是最短路径,所以需要借助邻接矩阵首先求出任意两个城市间的最短距离。这一步骤使用Floyd最短路径算法即可。然后,在此基础上来求出遍历各个城市后回到出发点的最短路径的距离,即求解TSP问题。

TSP问题目前有多种解法:搜索解法,动归解法,启发式解法。这里就针对poj 3311问题给出了前两种解法。

搜索解法:这种解法其实就是计算排列子集树的过程。从0点出发,要求遍历1,2,3点后回到0点。以不同的顺序来依次遍历1,2,3点就会导出不同的路径(0->1->2->3->0;0->1->3->2->0等等),总共有3!=6条路径需要考虑,从中选出最短的那条就是所求。搜索解法的时间复杂度为O(n!)

附上搜索代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <cstdio>
#include <cstdlib>

using namespace std;

int n;
vector<vector<int> > links;
vector<vector<int> > sp;
vector<bool> used;
long long ans;

void Floyed()
{
    sp = links;
    for(int k = 0; k < n; ++k)
    {
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n; ++j)
                sp[i][j] = min(sp[i][j], sp[i][k] + sp[k][j]);
        }
    }
    //for(int i = 0; i < n; ++i)
    //{
        //for(int j = 0; j < n; ++j)
            //cout << sp[i][j] << " ";
        //cout << endl;
    //}
}

void Backtrack(int level, int v, long long cost)
{
    if( level == n - 1 )
    {
        ans = min(cost + sp[v][0], ans);
        return;
    }

    for(int i = 0; i < n; ++i)
    {
        if( !used[i] )
        {
            used[i] = true;
            Backtrack(level + 1, i, cost + sp[v][i]);
            used[i] = false;
        }
    }
}

void Work()
{
    Floyed();
    ans = 1e8;
    used.assign(n, false);
    used[0] = true;
    Backtrack(0, 0, 0);
    //cout << "ans = ";
    cout << ans << endl;
}

int main()
{
    //freopen("3311.tst", "r", stdin);
    while( cin >> n && n )
    {
        ++n;
        //links.resize(n, vector<int>(n)); 将这一句替换为下面这一句,就会WA,还请高手能够指教!
        links.assign(n, vector<int>(n, 0));
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n; ++j)
                cin >> links[i][j];
        }
        Work();
    }
    return 0;
}

动归解法:仔细观察搜索解法的过程,其实是有很多重复计算的。比如从0点出发,经过1,2,3,4,5点后回到0点。那么0->1->2->(3,4,5三个点的排列)->0与0->2->1->(3,4,5三个点的排列)->0就存在重复计算(3,4,5三点的排列)->0路径集上的最短路径。只要我们能够将这些状态保存下来就能够降低一部分复杂度。下面就让我们用动归来求解这一问题。记dp(v, S)为从v点出发,遍历S集合中的每一个点后,回到出发点(0点)的最短距离。递推表达式的推导如下:

如果S为空集,即没有需要遍历的结点了。此时可以直接从v点回到0点,则dp(v,S)=sp[v][0] //sp[v][0]是v点到0点的最短路径距离

如果S不为空集,则dp(v,S)=min{sp[v][u] + dp(v,S-{u})}//sp[v][u]是v点到u点的最短路径距离

上述过程如何用编码实现呢,主要难点就在于集合S的表示。我们可以用位比特来表示一个集合。如集合{1,2,3},{1,2}分别可以用7(111),3(011)来表示。所以动归整个状态二维表的大小为n*2^n,而表中的每一个元素的计算需要O(n)的复杂度,所以动态规划的时间复杂度为O(n^2*2^n)

附上动态规划代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <cstdio>
#include <cstdlib>

using namespace std;

int n;
vector<vector<int> > links;
vector<vector<int> > sp;
vector<bool> used;
vector<vector<long long> > dp;
long long ans;

void Floyed()
{
    sp = links;
    for(int k = 0; k < n; ++k)
    {
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n; ++j)
                sp[i][j] = min(sp[i][j], sp[i][k] + sp[k][j]);
        }
    }
    //for(int i = 0; i < n; ++i)
    //{
        //for(int j = 0; j < n; ++j)
            //cout << sp[i][j] << " ";
        //cout << endl;
    //}
}

long long CalcVal(int v, long long bit)
{
    if( dp[v][bit] != -1 )
    {
        return dp[v][bit];
    }
    if( !bit )
    {
        dp[v][bit] = sp[v][0];
    }
    else
    {
        long long ret = 1e8;
        for(int i = 1; i < n; ++i)
        {
            int b = 1 << i - 1;
            if( b&bit )
            {
                ret = min(ret, sp[v][i] + CalcVal(i, b-bit));
            }
        }
        dp[v][bit] = ret;
    }
    return dp[v][bit];
}

void Work()
{
    Floyed();
    long long m = (1 << n - 1) - 1;
    dp.assign(n, vector<long long>(m, -1));
    ans = 1e8;
    for(int i = 1; i < n; ++i)
    {
        long long b = 1 << i - 1;
        ans = min(ans, sp[0][i] + CalcVal(i, b-m));
    }
    //cout << "ans = ";
    cout << ans << endl;
}

int main()
{
    //freopen("3311.tst", "r", stdin);
    while( cin >> n && n )
    {
        ++n;
        //links.resize(n, vector<int>(n));
        links.assign(n, vector<int>(n, 0));
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n; ++j)
                cin >> links[i][j];
        }
        Work();
    }
    return 0;
}

poj3311 经典tsp问题,布布扣,bubuko.com

时间: 2024-12-11 07:20:04

poj3311 经典tsp问题的相关文章

poj 3311 经典tsp问题,状压dp

题目链接:Hie with the Pie 解题思路: Floyd + 状态压缩DP题意是有N个城市(1~N)和一个PIZZA店(0),要求一条回路,从0出发,又回到0,而且距离最短也就是TSP(旅行商)问题,首先不难想到用FLOYD先求出任意2点的距离dis[i][j]接着枚举所有状态,用11位二进制表示10个城市和pizza店,1表示经过,0表示没有经过定义状态DP(S,i)表示在S状态下,到达城市I的最优值接着状态转移方程:DP(S,i) = min{DP(S^(1<<i-1),k) +

[状压dp]经典TSP

0出发 每个顶点经过一次 回到0 最小花费. 记忆化搜索: 1 // s: 已经访问过的节点状态 v: 当前位置 2 int dfs(int s, int v) 3 { 4 if(dp[s][v]>=0) 5 return dp[s][v]; 6 if(s==(1<<n)-1 && v==0) // 所有都走过 并 回到0 7 return dp[s][v]=0; 8 int ans=INF; 9 for(int u=0;u<n;u++) 10 if(!(s>

POJ3311——Hie with the Pie

Hie with the Pie Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4646   Accepted: 2457 Description The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortunately, due to cutbacks, they can affo

Hie with the Pie(poj3311)

题目链接:http://poj.org/problem?id=3311 学习博客:https://blog.csdn.net/u013480600/article/details/19692985 Hie with the Pie Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9954   Accepted: 5368 Description The Pizazz Pizzeria prides itself in de

poj 3311 状压DP

经典TSP变形 学到:1.floyd  O(n^3)处理随意两点的最短路 2.集合的位表示,我会在最后的总结出写出.注意写代码之前一定设计好位的状态.本题中,第0位到第n位分别代表第i个城市,1是已经走过,0没走过 那么DP方程  :dp[s][i]--当前在城市i.状态为s(s存储的是走过了那些城市) 3.最后要求形成回路,那么就是min(dp[1<<(n+1)-1][i],dp[0][i]) #include <cstdio> #include <cstring>

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

转载 ACM训练计划

leetcode代码 利用堆栈:http://oj.leetcode.com/problems/evaluate-reverse-polish-notation/http://oj.leetcode.com/problems/longest-valid-parentheses/ (也可以用一维数组,贪心)http://oj.leetcode.com/problems/valid-parentheses/http://oj.leetcode.com/problems/largest-rectang

poj题库分类

初期:一.基本算法:     (1)枚举. (poj1753,poj2965)     (2)贪心(poj1328,poj2109,poj2586)     (3)递归和分治法.     (4)递推.     (5)构造法.(poj3295)     (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996)二.图算法:     (1)图的深度优先遍历和广度优先遍历.     (2)最短路径算法(dijkstra,bellman-ford,floyd,hea

(转载)ACM训练计划,先过一遍基础再按此拼搏吧!!!!

ACM大量习题题库 ACM大量习题题库 现在网上有许多题库,大多是可以在线评测,所以叫做Online Judge.除了USACO是为IOI准备外,其余几乎全部是大学的ACM竞赛题库. USACO http://ace.delos.com/usacogate 美国著名在线题库,专门为信息学竞赛选手准备 TJU http://acm.tongji.edu.cn/ 同济大学在线题库,唯一的中文题库,适合NOIP选手 ZJU http://acm.zju.edu.cn/ 浙江大学在线题库 JLU htt