首页 > 代码库 > BZOJ 3714: [PA2014]Kuglarz

BZOJ 3714: [PA2014]Kuglarz

呃。。好像弃坑了好久=v=。。本来打算在bzoj每刷10题合起来写一份题解。。但是1个月好像还刷不到10题的样子(水题除外),所以还是单独写一下题解。。

Description

魔术师的桌子上有n个杯子排成一行,编号为1,2,…,n,其中某些杯子底下藏有一个小球,如果你准确地猜出是哪些杯子,你就可以获得奖品。花费c_ij元,魔术师就会告诉你杯子i,i+1,…,j底下藏有球的总数的奇偶性。
采取最优的询问策略,你至少需要花费多少元,才能保证猜出哪些杯子底下藏着球?

Input

第一行一个整数n(1<=n<=2000)。
第i+1行(1<=i<=n)有n+1-i个整数,表示每一种询问所需的花费。其中c_ij(对区间[i,j]进行询问的费用,1<=i<=j<=n,1<=c_ij<=10^9)为第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
题目大意:自行理解。。
算法:前缀和+最小生成树+奇技淫巧?
看到这题立刻就想到可以把每个杯底下是否有球看成一个01状态。。然后想了一个用前缀和贪心的办法。。然而行不通。。
然后默默翻开了题解。。
看的一脸懵逼。。
最后看懂了。。
因为要知道所有情况,所以要将这个01序列通过前缀和分成n块,那么就是怎么分割的问题了。。
所以可以抽象成一个图,知道i到j奇偶性的代价就是i连到j的边权,因为前缀和有0,所以n+1个点,连成最小代价,那么就是求这n+1个点的最小生成树了。。
只需要在读入的时候连一下边,然后跑一遍kruskal就好辣.>v<(表示扑通扑通跪倒在电脑前。。)
ps:以上为个人理解,有错误情指出=v=。。
代码:
技术分享
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int fa[10001];
 4 struct dist{
 5     int u,v,dis;
 6 }a[3000001];
 7 int find(int x){
 8     if(fa[x]!=x) fa[x]=find(fa[x]);
 9     return fa[x];
10 }
11 void u(int x,int y){
12     if(find(x)!=find(y)) fa[find(x)]=find(y);
13 }
14 bool cmp(dist a,dist b){
15     return a.dis<b.dis;
16 }
17 int main(){
18     int n,m,k,p=0;
19     long long ans=0;   
20     int c; 
21     scanf("%d",&n);
22     m=(n+1)*n/2;//边数
23 for(int i=1;i<=n;i++){
24     for (int j=i;j<=n;j++){
25     scanf("%d",&c);
26     p++;
27     a[p].u=i; a[p].v=j+1; a[p].dis=c;
28   }//建边
29 }
30     sort(a+1,a+m+1,cmp);
31     n++;
32 for(int i=1;i<=n;i++)
33     fa[i]=i;
34 for(int i=1;i<=m;++i){
35     if(find(a[i].u)!=find(a[i].v)){
36     u(a[i].u,a[i].v);
37     k++;
38     ans+=a[i].dis;
39 }
40 if(k==n-1) break;
41 }//kruskal
42 printf("%lld\n",ans);
43 }
View Code

 

原谅我代码写的丑。。原谅我缩进写的丑。。毕竟模板是从pascal翻译过来的。。还学了一波多关键字快排。。

 
 

 

BZOJ 3714: [PA2014]Kuglarz