目录
篛箕的自诉
以前的我对你爱搭不理,现在的我对你笔不能提:
以前学习搜索的时候看到这个题就感觉一定会做,对他爱搭不理。到现在回过头来看的时候,却发现自己已经忘记怎么做了(自己真是个弱鸡)。
题目分析+代码:
第一种做法(图论+搜索):
这个题给了我们一个图(不会图论的可以看下一种方法),对于每个节点的边权,我们可以将这个图分成两份,使得这两个图的边权尽可能的小,求这个最小值的最大值;
一想到最小值的最大值,这不就是二分答案吗?然而,问题的关键给到了cheak函数上,对于一个 mid ,我们要如何来确定这个mid的可行性?
首先,对于一个图,我们把边权大于 mid 的节点分开到两个监狱里面,边权小于等于mid的就不需要判断了,然后判断是否能把这个图分成二分图(又是一个图论的知识),这就可以用上板子了,判断一个图能否二分的板子:
这样就顺理成章的写出了nlogn复杂度的二分加二分图的判定,代码如下:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mem(arr, num) memset(arr, num, sizeof arr)
using namespace std;
const int N = 2e5 + 10, M = 1e6 + 10;
int n, m, e[M], ne[M], w[M], h[N], idx;
int color[N];//每个点的颜色;
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
bool dfs(int u, int c, int mid)
{
color[u] = c;
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (w[i] <= mid) continue;
if (color[j])
{
if (color[j] == color[u]) return 0;
}
else if (!dfs(j, 3 - c, mid)) return 0;
}
return 1;
}
bool cheak(int mid)//判断这个数值能否将这个图分成两幅图;
{
mem(color, 0);
for (int i = 1; i <= n; i ++ )
{
if (!color[i])
if (!dfs(i, 1, mid)) return 0;
}
return 1;
}
int main()
{
mem(h, -1);
scanf("%d%d", &n, &m);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
int l = 0, r = 1e9;
while (l < r)
{
int mid = l + r >> 1;
if (cheak(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", r);
return 0;
}
第二种做法(并查集,拓展域):
如果不会的可以先去看看这个题:食物链,很经典的并查集拓展域。
对于一对罪犯,如果他们的仇恨值较大的话,我们当然想把他们拆开,这样得到的仇恨值肯定会相应变小,我们就用这样的贪心的思路,来拆开仇恨值较大的两对罪犯。当我们拆到不能再拆的时候,那么这一对罪犯的仇恨值就是最后的仇恨值。
那么如何才能拆开一对罪犯呢?这时候就想到了并查集的扩展域,用一个域表示一个监狱,当我们拆分到不能拆分的时候输出这个仇恨值。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mem(arr, num) memset(arr, num, sizeof arr)
using namespace std;
const int N = 1e6 + 10;
int n, m, p[N];
struct node
{
int a, b, w;
} rela[N];
bool cmp(node x, node y)
{
return x.w > y.w;
}
int finds(int x)
{
if (p[x] != x) p[x] = finds(p[x]);
return p[x];
}
void init()
{
for (int i = 1; i <= 2 * n; i ++ ) p[i] = i;
}
int main()
{
scanf("%d%d", &n, &m);
init();
for (int i = 0; i < m ; i ++ )
scanf("%d%d%d", &rela[i].a, &rela[i].b, &rela[i].w);
sort(rela, rela + m, cmp);
for (int i = 0; i < m; i ++ )
{
int a = rela[i].a, b = rela[i].b, w = rela[i].w;
if (finds(a) == finds(b) || finds(a + n) == finds(b + n))
{
cout << w << endl;
return 0;
}
p[finds(a)] = finds(b + n), p[finds(b)] = finds(a + n);
}
cout << 0 << endl;
return 0;
}
这里,有一个很坑的点(我就被坑了很久),如果在循环里没有输出的话,这时候就代表两个监狱能够使所有有仇恨值的罪犯分开,这时候自然而然最小仇恨值为0。