UVa 12569 - Planning mobile robot on Tree (EASY Version)

问题

一棵4<=n<=15个节点的数。其中一个节点上一个机器人 还有些节点有石头, 指定机器人的起点和终点、以及石头的起始位置,求机器人最终到达终点最小步数的方案, 多解时输出一个即可. 每次机器人或者石头只能移动到相邻的空节点上。
eg: 有8个节点1\2\3\4\5\6\7\8
从1 能到达 2\6\7
从2 能到达 1\3\8
从3 能到达 2\4
从4 能到达 3\5

开始2\3\4节点上有石头
机器人的起始为1 终点为5

则至少16步 路径如下:
机器人 1-6
石头 2-1-7
机器人 6-1-2-8
石头 3-2-1-6
石头 4-3-2-1
机器人8-2-3-4-5

思路

  • 保证最短路径--IDA*算法
  • 保存每一步的状态 以及到达此状态时的一些信息
    用一个bit表示一个节点的状态[是否被占用] 最多有2^N个状态 每个状态中机器人的位置最多有N个;所以用二维数组存储,值是一个结构体;
struct stauts{

    // 到达此状态用的步数
    int cost;   

    // 对上一个状态pI 操作第a个bit=0 and 第b个bit=1 
    // 即移动机器人或者石头从a 节点到b节点后 达当前状态
    short a, b;   

    // 上一个状态和上一个机器人的状态
    int pI, prI;  
} S[1<<N][N];

测试案例 & 结果

第一行是节点个数 接下来是节点关系 接下来2 3 4 是石头的位置 接下来是1 5 机器人的起点和终点; 0表示结束;
8
6 1
1 7
1 2
2 3
2 8
3 4
4 5
0 0
2 3 4 0
1 5

结果:
机器人 1 6 
石头 2 1 7 
机器人 6 1 2 8 
石头 3 2 1 6 
石头 4 3 2 1 
机器人 8 2 3 4 5 

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 15

int G[N][N];        // storage edge link
int beginI, endI;   //机器人的起点和终点
int S0, SN=-1;      //初态 和 终态

int depthMax, depth;//当前允许的最大递归深度 当前深度

struct stauts{

    // 到达此状态用的步数
    int cost;

    // 对上一个状态pI 操作第a个bit=0 and 第b个bit=1
    // 即移动机器人或者石头从a 节点到b节点后 达当前状态
    short a, b;

    // 上一个状态和上一个机器人的状态
    int pI, prI;
};
struct stauts S[1<<N][N];


// 递归寻找
int DFS(int s, int rI)
{
    struct stauts *st=&S[s][rI];

    //printf("depth[%d][%d] s[%d] pas[%d] cost[%d] %d->%d robot[%d][%d][%d]\n",
    //      depthMax, depth, s, st->pI, st->cost, st->a, st->b, st->prI, rI, endI);
    //ppp(&s, 32);

    if(depth > depthMax) return 0;

    // 机器人到达终点
    if( rI == endI )
    {
        if ( SN==-1 || S[SN][endI].cost > S[s][endI].cost )
            SN = s;
    }

    // reached next status
    int i,j,u,v,sNew, rINew, sN;


    //遍历所有的节点
    for(u=0; u<N; u++)
    {
        //如果节点u被占用 上面有一个石头或者机器人
        if(s&1<<u)
        {
            // 遍历其余的节点 寻找能从u节点 移动到的&&还能被占用的节点v
            for(v=0; v<N; v++)if(G[u][v] && !(s&1<<v))
            {
                //获取转移后的状态sNew
                sNew=(s&(~(1<<u)))|(1<<v);

                //获取转移后的状态中机器人的状态rINew
                if(u==rI) rINew = v;
                else rINew = rI;

                //如果转移后的状态 还没被访问过 或者 访问过但步数太多;更新数据
                if( S[sNew][rINew].cost==-1 || S[sNew][rINew].cost > st->cost+1 )
                {
                    S[sNew][rINew].a=u;
                    S[sNew][rINew].b=v;
                    S[sNew][rINew].pI=s;
                    S[sNew][rINew].prI=rI;
                    S[sNew][rINew].cost = st->cost+1;
                    depth++;
                    DFS(sNew, rINew);
                    depth--;
                }
            }
        }
    }
    return 0;
}

int test12569()
{
    int i,j,k,u,v,m,n,s;
    scanf("%d", &n);

    // 获取边的关系
    memset(G, 0x00, sizeof(G));
    while(scanf("%d %d", &u, &v)==2)
    {
        if(u==0&&v==0)break;
        G[u-1][v-1]=G[v-1][u-1]=1;
    }

    //获取初始状态S0
    S0 = 0;
    while(scanf("%d", &u)==1)
    {
        if(u==0)break;
        S0 |= (1<<(u-1));
    }
    scanf("%d %d", &beginI, &endI);
    beginI--;
    endI--;
    S0 |= 1<<beginI;

    for( depthMax=0; SN==-1 && depthMax < 80; depthMax++)
    {
            memset(S, -1, sizeof(S));
            S[S0][beginI].cost=0;
            DFS(S0, beginI);
    }

    // 打印路径
    int ansI;
    int ans[1000];
    struct stauts *st;
    if(SN==-1)
    {
        printf("cann't reached\n");
    }
    else
    {
        memset(ans, -1, sizeof(ans));
        ansI=-1;
        int lastA=-1, lastB=-1;

        //倒序遍历路径 秉把路径 存储到ans中
        st = &S[SN][endI];
        while( st->pI != -1 )
        {
            if (st->b==lastA)
            {
                ans[++ansI]=lastA=st->a;
            }
            else
            {
                ++ansI;
                ans[++ansI]=st->b;
                ans[++ansI]=lastA=st->a;
            }
            // printf("%d %d\n", st->a , st->b);
            st = &S[st->pI][st->prI];
        }

        //输出答案
        v=beginI; k=0;
        while( ansI>=0 )
        {
            //k==1机器人走 k==0石头走
            if(ans[ansI]==v) k=1;
            else k=0;

            if(k==1) printf("机器人 ");
            else printf("石头 ");

            while(ansI!=-1 && ans[ansI]!= -1 )
            {
                if ( k==1 ) v=ans[ansI];
                printf("%d ", ans[ansI--]+1);
            }
            ansI--;
            printf("\n");
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值