BZOJ传送门
题目描述
魔术师的桌子上有
n
n
n个杯子排成一行,编号为
1
,
2
,
…
,
n
1,2,…,n
1,2,…,n,其中某些杯子底下藏有一个小球,如果你准确地猜出是哪些杯子,你就可以获得奖品。花费
c
i
j
c_{ij}
cij元,魔术师就会告诉你杯子
i
,
i
+
1
,
…
,
j
i,i+1,…,j
i,i+1,…,j底下藏有球的总数的奇偶性。
采取最优的询问策略,你至少需要花费多少元,才能保证猜出哪些杯子底下藏着球?
输入输出格式
输入格式
第一行一个整数
n
(
1
≤
n
≤
2000
)
n(1\le n\le 2000)
n(1≤n≤2000)。
第
i
+
1
i+1
i+1行
(
1
≤
i
≤
n
)
(1\le i\le n)
(1≤i≤n)有
n
+
1
−
i
n+1-i
n+1−i个整数,表示每一种询问所需的花费。其中
c
i
j
c_{ij}
cij(对区间
[
i
,
j
]
[i,j]
[i,j]进行询问的费用,
1
≤
i
≤
j
≤
n
,
1
≤
c
i
j
≤
1
0
9
1\le i\le j\le n,1\le c_{ij}\le 10^9
1≤i≤j≤n,1≤cij≤109)为第
i
+
1
i+1
i+1行第
j
+
1
−
i
j+1-i
j+1−i个数。
输出格式
输出一个整数,表示最少花费。
输入输出样例
输入样例#1:
5
1 2 3 4 5
4 3 2 1
3 4 5
2 1
5
输出样例#1:
7
解题分析
真是一道神题…
我们注意到, 如果我们能以某种方式得到 [ a , b ] [a,b] [a,b]和 [ b + 1 , c ] [b+1,c] [b+1,c](可以通过已知的相加减), 那我们肯定不会再去查询 [ a , c ] [a,c] [a,c]。我们将每个杯子看做一个点, 这就等价于我们选取了 a − 1 → b a-1\to b a−1→b和 b → c b\to c b→c的两条链, 并且在整个图中不会出现环。
这就是个 n + 1 n+1 n+1个点的最小生成树, 连边跑就好了。这里边有 O ( N 2 ) O(N^2) O(N2)条, 用 p r i m prim prim更好。
总复杂度 O ( N 2 ) O(N^2) O(N2)。
代码如下:
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <climits>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 2050
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int dis[MX];
bool vis[MX];
int mp[MX][MX];
int dot, nex, d;
long long ans;
void prim()
{
vis[0] = true;
for (R int i = 1; i <= dot; ++i) dis[i] = mp[0][i];
for (R int j = 1; j <= dot; ++j)
{
nex = 0, d = INT_MAX;
for (R int i = 1; i <= dot; ++i) if(dis[i] < d && !vis[i]) d = dis[i], nex = i;
vis[nex] = true; ans += dis[nex];
for (R int i = 1; i <= dot; ++i) dis[i] = std::min(dis[i], mp[nex][i]);
}
}
int main(void)
{
in(dot);
for (R int i = 1; i <= dot; ++i)
for (R int j = i; j <= dot; ++j)
in(mp[i - 1][j]), mp[j][i - 1] = mp[i - 1][j];
for (R int i = 0; i <= dot; ++i) mp[i][i] = INT_MAX;
prim();
printf("%lld", ans);
}