题目链接:找兔子
n的范围是[1, 15],可以用0 到 (1<<n)-1 的数表示全部状态,用dp[i] = t表示到达状态i的最少时间是t,对于每个点,如果它能到达的所有点在t秒时都已经确定了会不会有兔纸,那这个点就确定了在(t+1)s会不会有兔纸。对于每个时刻,可以询问<=2个点,所以由初始状态可以搜到最后确定能不能找到兔纸。
我没有想到找不到兔纸的数据...
关键是dp的思想==很巧妙。
附代码:
#include <stdio.h> #include <string.h> #include <iostream> #include <queue> #define maxn 16 #define inf 10000000 using namespace std; int edge[maxn]; // 储存每个点的边的信息 int dp[1<<maxn]; // dp[i]表示到达i状态的最少时间,一共有1<<n个状态 i的二进制中1表示已经确定了该点,0表示还没有 int main() { int t; cin >> t; while(t--) { int n, m; memset(edge, 0, sizeof(edge)); cin >> n >> m; for (int i=0; i<=(1<<n); ++i) { dp[i] = inf; } for (int i=0; i<m; ++i) { int x, y; cin >> x >> y; x--, y--; edge[x] |= (1<<y); edge[y] |= (1<<x); } for (int i=0; i<n; ++i) { if (!edge[i]) edge[i] |= (1<<i); } queue<int>que; dp[0] = 0; que.push(0); while(!que.empty()) { int now = que.front(); que.pop(); int nxt = 0; for (int i=0; i<n; ++i) { if ((now&edge[i]) == edge[i]) { nxt |= (1<<i); // 所有当前已经确定的点(不是状态)都保存在了nxt中。 } } for (int i=0; i<n; ++i) { for (int j=i+1; j<n; ++j) { int nxtstu = (nxt|(1<<i)|(1<<j)); //可以确定状态nxtstu 时间选择min if (dp[nxtstu] > dp[now] + 1) { dp[nxtstu] = dp[now] + 1; que.push(nxtstu);// 如果当前状态更新 加入队列以更新它可以到达的状态 } } } } int tot = (1<<n)-1; int ans = dp[tot]; //全部点都确定的一个状态 for (int i=0; i<n; ++i) { int stu = (tot^(1<<i)); //每个还有一个点没确定的状态 ans = min(ans, dp[stu]); } if (ans == inf) { cout << "-1\n"; } else cout << ans << endl; } return 0; }