题目
分析
part 1
思维难度挺大的一道题,建议先看看这篇blog及2005国家集训队ysy的解题报告(解题报告的链接在这篇blog里):
还有2006陈启峰的论文:一张一弛,解题之道
part 2
先由简,不考虑a[k],那么这样直接求怎么求呢?
直接枚举O(n2k)当然可以,只不过会超时;
有没有一种方法可以避免一些不必要的枚举呢?
这道题最大的阻碍就是这个绝对值问题了,因为它只跟两个生物的具体属性的相对大小有关,所以难以确定,必须枚举。
如果有办法把他给确定了,问题就好解决了。
part 3
看看 k 的数据范围,很容易想到可以从k下手;
抓住绝对值中的‘-’是不变的,也就是说,同一属性两种生物的符号是相反的
有什么用?(用处可大了)
说明对被减数取个负,他们的同一属性的符号就一样了呀。
这样的话,对于每个符号方案,求出每个生物的属性和,再相减不就可以了?
这个符号下放到每个属性,一共就有2k-1种符号方案(先不考虑第k种)
你可能会说,这不一定是绝对值
但是这里面一定包含绝对值,不是绝对值的答案一定会比正确答案小(废话因为是负数)
part 4
枚举每一种符号方案(可以用状压01串表示+-),求出里面的最大差就好,这个可以在O(n)的复杂度里求出来;
边枚举边维护一个min值,用当前值与min值得差更新答案,用当前值更新min值,这样这道题的大致框架就出来了
part 5
考虑a[k],它取的是绝对值的相反数,怎么考虑它呢?
如果它的符号也随机,按照枚举来,这样不能保证a[k]的贡献为负贡献,也不能保证最大差就是正确答案
那多出来的错误答案便是把a[k]弄成了正贡献;
要避免这类情况。
他的特征是小数减大数,我们需要一种方法,既满足最大差就是正确答案,又满足a[k]的性质;
不考虑a[k]的方法与生物的顺序无关,与某一种属性的大小也无关,但是a[k]就明显与大小有关了
a[k]必须保证是小数减大数,而其他的属性随意,这样就可以保证最大差就是正确答案;
既然a[k]与大小有关,不如就按照a[k]的大小确定加减,a[k]小的为被减数,而a[k]的符号确定为+。
这样做能否保证上述方法的正确性呢?
这样做,保证了第k种属性对答案的贡献为负,前面的符号仍然是枚举符号,而a[k]的符号确定,这样再求最大差,仍然是满足正确性
不如就按照a[k]的大小排序,保证a[k]大的在前面,只考虑减去比a[k]它大的生物,这样就可以按照part 4的方法在O(n)的时间中求出最大差
题解:
代码
1 /*************************** 2 User:Mandy.H.Y 3 Language:c++ 4 Problem:species 5 Algorithm: 6 Score: 7 ***************************/ 8 #include<bits/stdc++.h> 9 10 using namespace std; 11 12 const int maxn = 1e5 + 5; 13 const int maxk = 6; 14 15 int n,k,cur,ans,ans1,ans2,curid; 16 int c[maxk]; 17 18 struct Node{ 19 int a[10],id; 20 bool operator <(const Node &b)const { 21 return a[k] > b.a[k]; 22 } 23 }p[maxn]; 24 25 template<class T>inline void read(T &x){ 26 x = 0;bool flag = 0;char ch = getchar(); 27 while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); 28 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 29 if(flag) x = -x; 30 } 31 32 template<class T>void putch(const T x){ 33 if(x > 9) putch(x / 10); 34 putchar(x % 10 | 48); 35 } 36 37 template<class T>void put(const T x){ 38 if(x < 0) putchar('-'),putch(-x); 39 else putch(x); 40 } 41 42 void file(){ 43 freopen("species.in","r",stdin); 44 freopen("species.out","w",stdout); 45 } 46 47 void readdata(){ 48 read(n);read(k); 49 for(int i = 1;i <= k; ++ i) read(c[i]); 50 51 for(int i = 1;i <= n; ++ i){ 52 p[i].id = i; 53 for(int j = 1;j <= k; ++ j){ 54 read(p[i].a[j]); 55 p[i].a[j] *= c[j]; 56 } 57 } 58 } 59 60 void work(){ 61 sort(p + 1,p + n + 1); 62 ans = -2e9; 63 for(int i = 0;i < (1 << (k - 1)); ++ i){ 64 65 cur = p[1].a[k];curid = p[1].id; 66 for(int l = 1;l <= k - 1 ; ++ l){ 67 if(i & (1 << (l - 1))) cur += p[1].a[l]; 68 else cur -= p[1].a[l]; 69 } 70 71 for(int j = 2;j <= n; ++ j){ 72 int now = p[j].a[k]; 73 for(int l = 1;l <= k - 1 ; ++ l){ 74 if(i & (1 << (l - 1))) now += p[j].a[l]; 75 else now -= p[j].a[l]; 76 } 77 if(now - cur > ans){ 78 ans = now - cur; 79 ans1 = curid; 80 ans2 = p[j].id; 81 } 82 83 if(now < cur){ 84 cur = now; 85 curid = p[j].id; 86 } 87 } 88 } 89 put(ans1);putchar(' ');put(ans2);putchar(' '); 90 put(ans); 91 92 } 93 94 95 int main(){ 96 // file(); 97 readdata(); 98 work(); 99 return 0; 100 }