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
1 2 3 4 5
4 3 2 1
3 4 5
2 1
5
Sample Output
7
HINT
把杯子根据有没有球看成0/1的序列,题意是可以询问一段区间的和,要求确定每一个点的状态数
显然要一直取到长度为1的区间,才能确定这个点。
考虑把原来的序列划分成一些连续的区间,只要求出每一段的最小代价加上这样划分的代价就好了
这样变成不停的在序列上划分。显然每次划分都使得区间数+1,最终必须划分成n个长度为1的区间,因为要知道每一个点的状态
这样一来只需要考虑划分成n-1个区间的最小代价就好了
但是实际上划分的顺序对答案是不影响的,而且显然划分成n-1个区间就相当于连n-1条线
所以变成最小生成树了……真神奇
另外……因为a[i][j]实际上是把区间[i-1,j]提出来的代价,所以应该求0到n一共n+1个点的最小生成树
听说prim加个堆优化能跑得飞快,但是还没打,就写了个很sb的n^2prim
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<deque> 9 #include<set> 10 #include<map> 11 #include<ctime> 12 #define LL long long 13 #define inf 2147483647 14 #define pa pair<int,int> 15 #define pi 3.1415926535897932384626433832795028841971 16 #define N 2010 17 using namespace std; 18 int a[N][N]; 19 int near[N]; 20 bool mrk[N]; 21 int n; 22 LL ans; 23 inline LL read() 24 { 25 LL x=0,f=1;char ch=getchar(); 26 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 27 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 28 return x*f; 29 } 30 inline void prim() 31 { 32 mrk[0]=1; 33 for(int i=1;i<=n;i++) 34 near[i]=a[0][i]; 35 for (int i=1;i<=n;i++) 36 { 37 int mn=inf,fnd=0; 38 for (int j=1;j<=n;j++) 39 if (!mrk[j]&&near[j]<mn) 40 { 41 mn=near[j]; 42 fnd=j; 43 } 44 ans+=mn; 45 mrk[fnd]=1; 46 for (int j=1;j<=n;j++) 47 if (!mrk[j]) near[j]=min(near[j],a[fnd][j]); 48 } 49 } 50 int main() 51 { 52 n=read(); 53 for (int i=1;i<=n;i++) 54 for (int j=i;j<=n;j++) 55 a[i-1][j]=a[j][i-1]=read(); 56 prim(); 57 printf("%lld ",ans); 58 }