UVA - 12569 BFS 状压DP

题目大意:

给你一个无向图,一个机器人位于图中的点s位置,他的目标是点t位置,你可以移动他,每一次只能移动一个位置,图上有m个障碍物,这些障碍物也可以移动,这些障碍物分布在图上的不同点,任何一个点同一时刻只能有一个物品,不管是机器人还是障碍物。最后问最少需要移动几次(不只是机器人的移动,还包含障碍物的移动)。输出移动的过程,从哪个点到哪个点。

这个题说实话还挺难的,主要是因为题目中涉及到的可以移动的物品比较多,对于状态不好进行描述。一开始想的是每一步都尝试去移动机器人,机器人被堵住了再尝试去移动障碍物,但是发现这样的代码实现基本上是不可能的,其中的点太过于繁杂。

看了别人的代码才知道,其实我们没有必要去要求每一次移动的都是机器人。我们可以去尝试移动任何一个东西,不管他是机器人还是障碍物,因为BFS本来的思想也就是暴力,所以我们就尝试使用BFS去暴力的搜索就可以了,直到机器人到了目标位置,这个题的时间限制居然是10s。在搜索过程中还要记录每一次移动的哪个点的物品。

 

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

typedef struct{
    // s用来记录当前机器人在图中的哪个位置
    // t用来记录当前的障碍物的状态(包含机器人)
    // pre用来记录当前状态的上一个状态在队列的位置
    // length标记走到当前状态需要几步
    int s,t,pre,length;
}Node;

typedef struct{
    int size;
    int rear;
    int front;
    // 队列的大小要能容纳
    Node queue[1<<20];
}PriorityQueue;
PriorityQueue p;
int map[16][16];
// 标记某种状态是否被访问过
int vis[16][1<<15];
int n,m,s,t;

void push(Node d){
    p.size++;
    p.queue[p.rear++] = d;
}

Node pop(){
    Node res = p.queue[p.front++];
    p.size--;
    return res;
}

// 获取路径,根据不同状态来计算当前是把哪个障碍物从哪移动到哪
void getRes(int d){
	if (p.queue[d].pre != 0)
        getRes(p.queue[d].pre);
    // 开始状态
	int from_s = p.queue[p.queue[d].pre].t;
	// 结束状态
	int to_s = p.queue[d].t;
	int a, b;
	// 处于二进制数字的哪一位
	a = from_s ^ (from_s&to_s);
	b = to_s ^ (from_s&to_s);
	int u, k;
	// 计算该位置对应的图中的位置
	for (int i = 1; i <= n; i++){
		if ((1 << i)&a) u = i;
		if ((1 << i)&b) k = i;
	}
	printf("%d %d\n",u,k);
}

int main(){
    int count;
    scanf("%d",&count);
    memset(vis,0,sizeof(vis));
    for(int k=1;k<=count;k++){
        p.front = 0;
        p.rear = 0;
        p.size = 0;
        memset(map,0,sizeof(map));
        scanf("%d%d%d%d",&n,&m,&s,&t);
        int state = 0;
        for(int i=0;i<m;i++){
            int c;
            scanf("%d",&c);
            state |= (1<<c);
        }
        for(int i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            map[x][y] = 1;
            map[y][x] = 1;
        }
        // 开始时障碍的布局
        state |= (1 << s);
        // 标记当前状态已经被访问过
        // 为什么要用二维数组?因为我们不能仅仅通过一维01不能判断重复该状态是否出现过
        // 虽然一个一维数组中可能状态值相同,但是机器人的位置可能并不同
        // 所以需要一个二维数据来标记机器人在不同位置时的障碍物
        vis[s][state] = k;
        Node node;
        node.s = s;
        node.t = state;
        node.pre = -1;
        node.length = 0;
        push(node);
        Node res;
        res.length = -1;
        while(p.size>0){
            Node node = pop();
            // 如果当前点位于终点
            if(node.s == t){
                res = node;
                break;
            }
            for(int i=1;i<=n;i++){
                // 当前i位置存在障碍物,尝试移动当前位置的障碍物
                // 注意这个障碍物可能是机器人
                if((1 << i)&node.t){
                    for(int j=1;j<=n;j++){
                        if(map[i][j]){
                            // 当前要移动到的位置存在障碍物
                            if((1<<j) & node.t){
                                continue;
                            }
                            int curS = node.s,
                            // 新状态
                            curState = ((node.t | (1<<j)) ^ (1 << i));
                            // 当前障碍是机器人
                            if(curS == i){
                                // 机器人移动到新的位置了
                                curS = j;
                            }
                            if(vis[curS][curState]!=k){
                                vis[curS][curState] = k;
                                Node newNode;
                                newNode.t = curState;
                                newNode.s = curS;
                                newNode.pre = p.front-1;
                                newNode.length = node.length+1;
                                push(newNode);
                            }
                        }
                    }
                }
            }
        }
        printf("Case %d: ",k);
        if(res.length == -1){
            printf("-1\n");
        }else{
            printf("%d\n",res.length);
            getRes(p.front-1);
        }
        printf("\n");
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值