魔术师的桌子上有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
解析:
确定每个位置是否有小球等价于确定所有的前缀的奇偶性。
对于前缀i和前缀i+1:
如果奇偶相同,i不存在小球。
如果奇偶不同,i存在小球。
于是我们只需要用n次询问,将n+1个点连通,就可推出点与点之间相互的关系。
考虑到本题为稠密图,故采用Prim算法来求最小生成树。
代码如下:
#include<bits/stdc++.h>
using namespace std;
bool f[2010];
int n,v[2010][2010],d[2010],m,minn;
long long ans;
int main() {
scanf("%d",&n);
for(inti=1;i<=n;i++)
for(intj=i;j<=n;j++)
scanf("%d",&v[i-1][j]),v[j][i-1]=v[i-1][j];
for(inti=1;i<=n;i++) d[i]=1e9;
d[0]=0;
while(1) {
minn=1e9;
for(int i=0;i<=n;i++)
if(!f[i]&&d[i]<minn)
minn=d[i],m=i;
if(minn==1e9) break;
ans+=minn;
f[m]=1;
for(int i=0;i<=n;i++)
if(v[m][i]<d[i]&&!f[i])
d[i]=v[m][i];
}
printf("%lld\n",ans);
return 0;
}
但也可以用Kruskal算法:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[20000010];
int tot;
struct node {
int x,y;
long long val;
} a[20000010];
long long ans;
int find(int x) {
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
bool cmp(node x,node y) {
return x.val<y.val;
}
signed main() {
cin>>n;
for(int i=1; i<=n; i++) fa[i]=i;
for(int i=1; i<=n; i++) {
for(int j=i; j<=n; j++) {
tot++;
a[tot].x=i-1;
a[tot].y=j;
cin>>a[tot].val;
}
}
sort(a+1,a+tot+1,cmp);
int sum=0;
for(int i=1; i<=tot; i++) {
int x=a[i].x,y=a[i].y,val=a[i].val;
int fx=find(x),fy=find(y);
if(fx!=fy) {
sum++;
fa[fx]=fy;
ans+=val;
}
if(sum>=n) break;
}
cout<<ans<<endl;
return 0;
}