>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下面有没有球,有两种求法:
所以为什么是最小生成树?
我们把第 i i i个杯子转化成第 i − 1 i-1 i−1点到第 i i i点之间的一条线,所以我们要把0~n点都合并到同一集合(因为第1点变成了0 ~ 1之间的一条线,所以我们要添加一个点0),
对于输入的 c [ i ] [ j ] c[i][j] c[i][j],显而易见,我们要添加的是第 i − 1 i-1 i−1点到第 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;
}