文章标题 CSU 1843: Jumping monkey (状态压缩+dp)

本文介绍了一个经典的状压动态规划问题——猴子跳跃。在一个包含21个点的图中,通过猎人的攻击策略来确保捕捉到一只不断在点间跳跃的猴子。文章详细解释了如何使用状态压缩技术来解决问题,并提供了完整的C++代码实现。

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

1843: Jumping monkey

链接 1843: Jumping monkey
题意::有21个点,点之间有连边。现在有一只猴子, 初始位置不清楚。猎人每次可以攻击任意一点。每次 攻击后,若打中猴子则结束,没打中则猴子必定会移 动到当前位置的相邻点的某一个。问是否存在一个攻 击序列,使得必然打中猴子,若有多种可能还需输出 攻击次数最少的解,若还相同则输出字典序最小的解
分析:点数n只有21明显可以状压。用状态st表示当前猴子可能的位置, 初始状态为(1<

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<math.h>
#include<map>
#include<queue> 
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;

int n,m;
vector <int> g[25]; 
int vis[1<<22];
int shoot[1<<22];
int pre[1<<22];

int bfs(int st){
    queue<int>q;
    vis[st]=1;
    q.push(st);
    while (!q.empty()){
        int tmp=q.front();q.pop();
        if (!tmp)return true;
        for (int i=0;i<n;i++){//枚举射击的点,射击的点也有可能是当前猴子不再的点,所以得遍历所有的点 
            int nst=0;//用来保存射击后的猴可能跑到那个状态 
            for (int j=0;j<n;j++){//枚举猴子可能在的点(即状态为1) 
                if (j!=i&&(tmp&(1<<j))){//判断当前状态中猴子是否可能在j这个点 
                    for (int k=0;k<g[j].size();k++){//如果在的话,下一枪可能跑到其邻接点 
                        int v=g[j][k];//直接或就行 
                        nst|=(1<<v);
                    }
                }
            }
            if (vis[nst])continue;//如果当前状态已经有了,就跳过 
            vis[nst]=1;//标记这个状态已经有了 
            pre[nst]=tmp;//保存当前这个状态的父节点为tmp 
            shoot[nst]=i;//保存这个状态是打的第i个点 
            q.push(nst);
        }
    } 
    return 0;
}

int main ()
{
    while(scanf ("%d%d",&n,&m)){
        if (n==0&&m==0) break;
        for (int i=0;i<=n;i++){
            g[i].clear();
        }
        int u,v;
        for (int i=0;i<m;i++){
            scanf ("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        int st=(1<<n)-1;
        for (int i=0;i<=st;i++){
            vis[i]=0;
        }
        if (bfs(st)){
            vector<int>ans;
            int now=0;
            while (1){
                if (now==st)break;//到达初始状态则停止 
                ans.push_back(shoot[now]);//将每个状态打的点放进向量 
                now=pre[now];
            }
            printf ("%d:",ans.size());
            for (int i=ans.size()-1;i>=0;i--){
                printf (" %d",ans[i]);
            }
            printf ("\n");
        }
        else {
            //cout<<"";
            printf ("Impossible\n");
        }
    }   
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值