挑战程序设计竞赛 折半枚举
Posted TURNINING
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了挑战程序设计竞赛 折半枚举相关的知识,希望对你有一定的参考价值。
折半枚举的思想就是如果直接暴力复杂度太大,我们可以先枚举一半,再通过这些值再枚举另外一半算出结果。
传送门
poj 2785 4 Values whose Sum is 0
题意:有四个数组,每个数组的大小都为n,要求从各个数组中各找一个数使a + b + c + d = 0;
思路:a + b + c + d = 0 -> a + b = -(c + d), 我们可以枚举a数组和b数组所有和的结果,然后再枚举c + d的同时用二分找到符合结果的个数。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 4000 + 10;
const int INF = 0x3f3f3f3f;
const int MAX_LOG_N = 20;
const double eps = 1e-6;
const int mod = 1e9 + 7;
int n;
int A[maxn], B[maxn], C[maxn], D[maxn];
int CD[maxn * maxn];
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d %d %d %d", &A[i], &B[i], &C[i], &D[i]);
//折半枚举C + D
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
CD[i + (j-1) * n] = C[i] + D[j];
}
}
sort(CD+1, CD+1+n*n);
LL res = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int t = -(A[i] + B[j]);
//与t相等的个数
res += upper_bound(CD+1, CD+1+n*n, t) - lower_bound(CD+1, CD+1+n*n, t);
}
}
printf("%lld\\n", res);
}
int main() {
//ios::sync_with_stdio(false);
int t = 1;
while(t--) {
solve();
}
return 0;
}
超大背包
由于这题的n很小,但w特别大,用01背包无法求解,我们可以先考虑枚举一半物品的的所有结果(2^(n/2)),同上题,在枚举另外一半的同时找到和不超过W但v最大的前一半里的结果。排序后做一个处理就行了。注意:对多个权值时,lower_bound是根据字典序来查找的。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> P;
const int maxn = 40;
const LL INF = 1e17;
const double eps = 1e-6;
const LL mod = 1e9 + 7;
int n;
LL W;
LL v[maxn], w[maxn];
P ps[1 << 20];
void solve() {
scanf("%d", &n);
int n2 = n / 2;
for(int i = 0; i < n; i++) scanf("%lld", &w[i]);
for(int i = 0; i < n; i++) scanf("%lld", &v[i]);
scanf("%lld", &W);
for(int i = 0; i < 1 << n2; i++) {
LL sw = 0, sv = 0;
for(int j = 0; j < n2; j++) {
if(i >> j & 1) {
sw += w[j];
sv += v[j];
}
}
ps[i] = make_pair(sw, sv);
}
sort(ps, ps + (1 << n2));
int m = 1;
//保证v随i的增大而增大。
for(int i = 1; i < 1 << n2; i++) {
if(ps[m - 1].second < ps[i].second) {
ps[m++] = ps[i];
}
}
LL res = 0;
for(int i = 0; i < 1 << (n - n2); i++) {
LL sw = 0, sv = 0;
for(int j = 0; j < (n - n2); j++) {
if(i >> j & 1) {
sw += w[n2 + j];
sv += v[n2 + j];
}
}
LL temp = 0;
if(sw <= W) {
temp = (lower_bound(ps, ps + m, make_pair(W - sw,INF)) - 1)->second;
res = max(res, temp + sv);
}
}
printf("%lld\\n", res);
}
int main() {
int t = 1;
while(t--) {
solve();
}
return 0;
}
以上是关于挑战程序设计竞赛 折半枚举的主要内容,如果未能解决你的问题,请参考以下文章