题意就是:
一个猎人在森林里捕猎。
然后有只猴子,喜欢躲树的后边,猎人看不见它
然后给出了一张图,表示有哪些树是相邻的。
数据保证任意图中两个树都可以直接或间接的到达
猎人有一个枪,每次他可以选择一颗树,然后射子弹。如果猴子躲那个树后边,就被打死了。
但是如果没躲那个树后边,猴子会利用猎人换子弹的时间立刻蹦到这个树相邻的树上躲藏,
问猎人应该怎样击打树才能保证必然能击打中猴子。
比如第一个样例,
猎人只要在0号树上击打两次即可,因为猴子如果没被打中,必然会往旁边的树跑
然后可以看到的是
n的范围很小。
可以考虑使用状态压缩DP
刚开始因为猴子是有可能出现在所有树上的。
所以状态为(1 << n) - 1
我们的目标是 让猴子出现的可能消灭为0状态
对于猴子每次枪响后转移
对于一个点,必然是其周围所有的点都有可能会跳到这个点上
那么我们每次先将猴子转移后的状态求出来。
选择一个尽量小的点毙掉这颗树上的猴子。
因为没有什么顺序
所以将状态加入队列中来转移
如果转移到了0状态。
因为我们每次都尽量小的点去毙掉
所以能保证字典序,中间过程记录下路径,最后输出即可
#include <iostream> #include <cstring> #include <string> #include <algorithm> #include <cmath> #include <cstdio> #include <queue> #include <vector> #define eps 1e-8 #define INF 111111111 using namespace std ; int n, m; queue<int>q; vector<int>res; int st[33]; int pre[(1 << 21) + 5]; int num[(1 << 21) + 5]; int gao() { q.push((1 << n) - 1); while(!q.empty()) { int u = q.front(); q.pop(); int nxt = 0; for(int i = 0; i < n; i++) { if(u & (1 << i)) nxt |= st[i]; } for(int i = 0; i < n; i++) if(nxt & (1 << i)) { int k = nxt ^ (1 << i); if(pre[k] == -1) { pre[k] = u; num[k] = i; q.push(k); if(k == 0) return 1; } } } return 0; } int main() { int x, y; while(scanf("%d%d", &n, &m) != EOF) { if(!n && !m) break; memset(st, 0, sizeof(st)); while(!q.empty()) q.pop(); for(int i = 0; i < m; i++) { scanf("%d%d", &x, &y); st[x] |= (1 << y); st[y] |= (1 << x); } if(n == 1) { printf("1: 0 "); continue; } if(m >= n) { puts("Impossible"); continue; } memset(pre, -1, sizeof(pre)); int k = gao(); if(k == 0) puts("Impossible"); else { res.clear(); int top = 0; while(top != (1 << n) - 1) { res.push_back(num[top]); top = pre[top]; } printf("%d:", res.size()); for(int i = res.size() - 1; i >= 0; i--) printf(" %d", res[i]); puts(""); } } return 0; }