UVA10944 Nuts for nuts..(状压dp)

比较经典的\(TSP\)问题

题目
题意:松鼠位于\(L\)点,需要采集所有#点的松果,最后返回\(L\)点,问此过程的最短距离(松鼠有8种转移方式,即上下左右+对角线1个单位)

思路:一看此题就是和售货员的难题如出一辙,只是输入方法不一样,如果做过的可以直接套版转化
用二进制数表示坚果的收集状态,\(0\)表示未收集,\(1\)已收集;\(dis\)[\(i\)][\(j\)]表示节点\(i\)\(j\)的相对距离;\(f\)[\(i\)][\(j\)]表示在收集状态为\(j\)是收集\(i\)的最小步数;
显然,收集每颗坚果的最小步数为\(f\)[\(i\)][\(2^{i-1}\)]=\(dis\)[\(0\)][\(i\)];
递增枚举状态值\(i\),状态\(i\)中最后被收集的坚果\(j\),枚举\(i\)外的坚果\(k\)
\(f\)[\(k\)][\(i\)+\(1\)<<(\(k\)-\(1\))] = \(min\)(\(f\)[\(k\)][\(i\)+\(1\)<<(\(k\)-\(1\))], \(f\)[\(j\)][\(i\)] + \(dis\)[\(j\)][\(k\)]);
所有坚果收集后,若最后一颗为\(i\),则到\(i\)的最小步数为\(f\)[\(i\)][\(1\)<<(\(n\))-\(1\)],加上返回起点的步数\(map\)[\(0\)][\(i\)],找到最少步数;
\(ans\)=\(min\)(\(f\)[\(i\)][\(1\)<<(\(n\))-\(1\)]+\(dis\)[\(0\)][\(i\)]);(枚举\(i\))
本题数据范围可能有误,建议数组稍微开大一点

#include<stdio.h>
#include<cmath>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;

int n,m;const int MAXN = 30;
char map[MAXN][MAXN];int u;int dis[MAXN][MAXN];
int l[MAXN];int f[MAXN][1<<20];

struct edge{
    int i,j;
}e;edge nut[MAXN];

inline void init(){
    getchar();
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            scanf("%c",&map[i][j]);
            if(map[i][j] == '#'){
                e.i = i;e.j = j;
                nut[++u] = e;
            }
            if(map[i][j] == 'L'){
                e.i = i;e.j = j;
                nut[1] = e;
            }
        }
        getchar();
    }
}

inline void work(){
    for(int i=1;i<=u;++i){
        for(int j=i+1;j<=u;++j){
            dis[i][j] = dis[j][i] = max(abs(nut[i].i - nut[j].i) , abs(nut[i].j - nut[j].j));
        }
    }
}

inline void get(){
    memset(f,inf,sizeof f);
    f[1][1]=0;int p=(1<<u);
    for(int i=0;i<p;++i){
        for(int j=1;j<=u;++j){
            if(i&l[j]){
                for(int k=1;k<=u;++k){
                    if(!(i&l[k])){
                        f[k][i|l[k]] = min(f[k][i|l[k]],f[j][i] + dis[j][k]);
                    }
                }
            }
        }
    }
}

inline void clear(){
    u = 1;
    memset(f,0,sizeof f);
    memset(dis,0,sizeof dis);
    memset(l,0,sizeof l);
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        clear();
        init();
        work();
        for(int i=1;i<=u;++i) l[i] = (1<<i-1);
        get();
        int ans=inf;
        for(int i=1;i<=u;++i){
            ans = min(ans,f[i][(1<<u)-1] + dis[i][1]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/lajioj/p/9318673.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值