Kuglarz【并查集】【最小生成树】

>Description
魔术师的桌子上有 n 个杯子排成一行,编号为 1,2,…,n,其中某些杯子底下藏有一个小球,如果你准确地猜出是哪些杯子,你就可以获得奖品。

花费 cij 元,魔术师就会告诉你杯子 i,i+1,…,j 底下藏有球的总数的奇偶性。

采取最优的询问策略,你至少需要花费多少元,才能保证猜出哪些杯子底下藏着球?


>Input
第一行一个整数 n。
第 i+1 行(1≤i≤n)有 n+1−i 个整数,表示每一种询问所需的花费。
其中 cij​(对区间 [i,j] 进行询问的费用,1≤i≤j≤n)为第 i+1 行第 j+1−i 个数。

>Output
输出一个整数,表示最少花费。


>Sample Input
5
1 2 3 4 5
4 3 2 1
3 4 5
2 1
5

>Sample Output
7


>解题思路
神奇最小生成树

由题目可以看出来,要求一个杯子 i i i下面有没有球,有两种求法:

  1. 花费 c [ i ] [ i ] c[i][i] c[i][i]
  2. 花费 c [ i ] [ j ] + c [ i + 1 ] [ j ] c[i][j]+c[i+1][j] c[i][j]+c[i+1][j]( o r c [ j ] [ i ] + c [ j ] [ i − 1 ] or c[j][i]+c[j][i-1] orc[j][i]+c[j][i1])

所以为什么是最小生成树?
我们把 i i i个杯子转化成第 i − 1 i-1 i1点到第 i i i点之间的一条线,所以我们要把0~n点都合并到同一集合(因为第1点变成了0 ~ 1之间的一条线,所以我们要添加一个点0),
对于输入的 c [ i ] [ j ] c[i][j] c[i][j],显而易见,我们要添加的是 i − 1 i-1 i1点到第 j j j的一条路径,如:
求第三个杯子下面有没有球,有两种做法:1.连接(2,3);2.连接(0,3)和(0,2)。这样操作后合并到同一集合的为2和3,指的就是第三个杯子


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;

struct ooo
{
	ll c, x, y;
} a[4000005];
ll n, c[2005], t, w, ans;

bool bmp (ooo aa, ooo bb) {return aa.c < bb.c;}
ll find (ll now)
{
	if (c[now] == now) return now;
	return c[now] = find (c[now]);
}

int main()
{
	scanf ("%I64d", &n);
	for (ll i = 1; i <= n; i++)
	  for (ll j = i; j <= n; j++) //注意输入
	  {
	  	scanf ("%I64d", &w);
	  	a[++t].c = w; a[t].x = i - 1; a[t].y = j;
	  }
	sort (a + 1, a + 1 + t, bmp);
	for (ll i = 0; i <= n; i++) c[i] = i;
	for (ll i = 1; i <= t; i++)
	{
		ll u = find (a[i].x), v = find (a[i].y);
		if (u != v)
		{
			ans += a[i].c; //累计答案
			c[u] = v;
		}
	}
	printf ("%I64d", ans);
	return 0;
}

你可能感兴趣的:(最小生成树,并查集)