hdu 4568 (状压dp TSP问题)

本文介绍了一种解决特定旅行商问题(TSP)的方法,该问题涉及寻找访问指定点集并返回起点的最短路径。通过构建图、计算点间距离及应用状态压缩动态规划算法来解决这一挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题意:给出一个二维宝藏图,有些点为-1代表不能走,否则的话代表挖这个点的宝藏需要的花费。然后给出k个点,问经过出边界出发经过这k个点,再从边界结束的最小花费是多少?从哪个边界出发和结束不限制。

思路:

首先想到的就是tsp问题,不过这里首先要建边,求出这k个点到各个点的距离和到边界的最小距离。然后跑tsp的状压dp.dp[i][s]代表这个跑完s集合以i结束的最小花费,那么当j也属于这个集合的时候,那么除了i的集合以j结束的状态就可以更新dp[i][s],状态转移方程为dp[i][s] = minI(dp[i][s],dp[j][s ^(1 <<(j - 1))]));


PS :输入的时候可以以线性输入的,现在二维输入的,搞得代码有点丑。


#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
#define clr(x,y) memset(x,y,sizeof x)
const int maxn =  200 + 10;
#define INF 0x3f3f3f3f
int maps[maxn][maxn];
P a[maxn];
int dist[20][maxn * maxn];
int head[maxn * maxn * 10];
struct Edge
{
    Edge(){}
    Edge(int x,int y,int z):to(x),w(y),next(z){}
    int to,w,next;
}edge[maxn * maxn * 10];
int edge_num;
int dp[20][1 << 15];
int boundary[maxn * maxn];
int d[maxn];
void Init()
{
    clr(head,-1);
    edge_num = 0;
}
void add_edge(int x,int y,int z)
{
    edge[edge_num] = Edge(y,z,head[x]);
    head[x] = edge_num ++;
}
int dijkstra(int s,int pos)
{
    priority_queue<P,vector<P>,greater<P> >q;
    dist[pos][s] = 0;
    q.push(P(0,s));
    while(!q.empty())
    {
        P t = q.top();q.pop();
        int u = t.second;
        if(t.first > dist[pos][u])
            continue;
        for(int i = head[u]; i != -1;i = edge[i].next)
        {
            int v = edge[i].to,w = edge[i].w;
            if(dist[pos][v] > dist[pos][u] + w)
            {
                dist[pos][v] = dist[pos][u] + w;
                q.push(P(dist[pos][v],v));
            }
        }
    }
}

int main()
{
    int Tcase;
    scanf("%d",&Tcase);
    while(Tcase --)
    {
        Init();
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i = 0; i < n; i ++)
            for(int j = 0; j < m; j ++)
            scanf("%d",&maps[i][j]);
        int len = 0;
        for(int i = 0; i < m; i ++)
        {
            boundary[len ++] = i;boundary[len ++] = (n - 1) * m + i;
        }
        for(int i = 0; i < n; i ++)
        {
            boundary[len ++] = i * m;boundary[len ++] = i * m + m - 1;
        }
        sort(boundary,boundary + len);
        len = unique(boundary,boundary + len) - boundary;
        for(int i = 0; i < n; i ++)
        {
            for(int j = 0; j < m; j ++)
            {
                int temp = i * m + j;
                if(maps[i][j] != -1)
                {
                    if(j > 0 && maps[i][j - 1] != -1)
                    add_edge(temp,temp - 1,maps[i][j - 1]);
                    if(j + 1 < m && maps[i][j + 1] != -1)
                    add_edge(temp,temp + 1,maps[i][j + 1]);
                    if(i > 0 && maps[i - 1][j] != -1)
                    add_edge(temp,temp - m,maps[i - 1][j]);
                    if(i + 1 < n&& maps[i + 1][j] != -1)
                    add_edge(temp,temp + m,maps[i + 1][j]);
                }
            }
        }
        int k;
        scanf("%d",&k);
        for(int i = 1; i <= k;i ++)
            scanf("%d%d",&a[i].first,&a[i].second);
        for(int i = 1; i <= k;i ++)
        {
            clr(dist[i],INF);
            dijkstra(a[i].first * m + a[i].second,i);
        }
        for(int i = 1; i <= k; i ++)
        {
            d[i] = INF;
            for(int j = 0; j < len; j ++)
            {
                if(a[i].first * m + a[i].second == boundary[j])
                {
                    d[i] = 0;
                    break;
                }
                d[i]= min(d[i],dist[i][boundary[j]]);
            }
        }
        clr(dp,INF);
        for(int s = 0; s < 1 << k; s ++)
        {
            for(int i = 1; i <= k; i ++)
            {
                if((1 << (i - 1)) & s )
                {
                    if((1 << (i - 1)) == s)
                    {
                        dp[i][s] = d[i] + maps[a[i].first][a[i].second];
                    }
                    for(int j = 1;j <= k; j ++)
                    {
                        if((1 << (j - 1) & s) && j != i)
                        {
                            dp[i][s] = min(dp[i][s],dp[j][s ^(1 <<(i - 1))] + dist[j][a[i].first * m + a[i].second]);
                        }
                    }
                }
            }
        }
        int ans = INF;
        for(int i = 1; i <= k; i ++)
        {
            ans = min(ans,dp[i][(1 << k) - 1] + d[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值