题解:P3557 [POI 2013] GRA-Tower Defense Game
给定一个图,选定不多于
k 个点,使得图中每个点都离某一个选定的点距离不多于\bm 2 。无需最小化选点数量。保证可以选定不多于
k 个点,使得图中每个点都离某一个选定的点距离不多于\bm 1 。
:::warning[这是错误做法]
既然有了后面的保证,那么就考虑后面那个题怎么做?
省流:NP 完全的,目前没有发现多项式时间做法。实际上如果你找到了多项式复杂度做法,或者证明了不存在,那么快去领图灵奖吧!
好消息,这个问题是一个经典的问题。坏消息:
当然,如果你不相信 AI,那么百度百科:
:::
那么我们就需要使用下面的条件了。大胆贪心!每次选出一个没有满足条件的点,选择这个点!
这对吗?这能过,实现方法后面讲。这真的对吗?
考虑证明。不妨设我们的解的点集为
:::info[实现方法]{open}
DFS。从小到大枚举每个点,如果没有被标记过则 dfs(不考虑标记,考虑标记正确性就不对了)将所有与其距离不多于
看起来一个菊花图就能卡掉是吧!实际上菊花图上选任意一个节点都可以完成。你是卡不掉的。
时间复杂度证明就是,每个点最多和被选中的点相邻一次,也就是只会被遍历一次所有邻居,总时间复杂度就是度数之和
:::success[代码&提交记录]
Fun fact:代码和
#include <cstdio>
#include <vector>
using namespace std;
vector<int> web[500005];
bool vis[500005];
void dfs(int u, int d = 0) // d = depth = 深度
{
vis[u] = true;
if (d == 2) return;
for (int v : web[u])
{
dfs(v, d + 1);
}
}
int main()
{
int n, m;
scanf("%d%d%*d", &n, &m); // 冷门小技巧:%*d 可以跳过一个数字,%*c %*s 等同理
for (int i = 1; i <= m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
web[x].push_back(y);
web[y].push_back(x);
}
vector<int> ans;
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
ans.push_back(i);
dfs(i);
}
}
printf("%d\n", ans.size());
for (int i : ans)
{
printf("%d ", i);
}
return 0;
}
record。 :::