题目链接

状态压缩

|x-y| 等价于 max(x-y, y-x)

对于ki的符号是取正还是取负,可以用二进制的0和1来表示,那么每一个主武器和副武器的属性都有 \(2^8\) 种状态,如果主武器的 ki 取负,那么副武器的ki一定得取正才行(对应上面的公式),只要我们能分别预先求出主武器和副武器那 \(2^8\) 种状态中,每一种状态的最大取值,那么通过比较主武器的某个k状态x的取值加上副武器中x的取反值的和,取最大值就是答案

i取反 = 与i互补,即 i取反 = \(2^8\) -1-i

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int Maxn = 1e5+10;
const long long INF = 1e18;

int nk[Maxn][10], mk[Maxn][10], ns[Maxn], ms[Maxn];
long long N[40], M[40];

int main(void)
{
	int T;
	scanf("%d", &T);
	while(T--) {
		int n, m, k;
		scanf("%d%d%d", &n, &m, &k);
		for(int i = 0; i < n; ++i) {
			scanf("%d", &ns[i]);
			for(int j = 0; j < k; ++j) scanf("%d", &nk[i][j]); 
		}
		for(int i = 0; i < m; ++i) {
			scanf("%d", &ms[i]);
			for(int j = 0; j < k; ++j) scanf("%d", &mk[i][j]);
		}
		for(int i = 0; i < (1<<k); ++i) N[i] = M[i] = -INF;
		
		for(int i = 0; i < n; ++i) {
			for(int j = 0; j < (1<<k); ++j) {
				long long tmp = ns[i];
				for(int l = 0; l < k; ++l) {
					if((j>>l)&1) tmp -= nk[i][l];
					else tmp += nk[i][l];
				}
				N[j] = max(N[j], tmp);
			}
		}
		for(int i = 0; i < m; ++i) {
			for(int j = 0; j < (1<<k); ++j) {
				long long tmp = ms[i];
				for(int l = 0; l < k; ++l) {
					if((j>>l)&1) tmp -= mk[i][l];
					else tmp += mk[i][l];
				}
				M[j] = max(M[j], tmp);
			}
		}
		long long ans = -INF;
		for(int i = 0; i < (1<<k); ++i) ans = max(ans, N[i]+M[(1<<k)-1-i]);
		printf("%lld\n", ans);
	}	
	return 0;
 }