我想知道New Lottery Game问题的有效方法。
彩票正在改变!彩票曾经有一台机器来生成随机中奖号码。但由于作弊问题,彩票公司决定增加另一台机器。新的中奖号码将是两台机器生成的两个随机数之间的按位与运算的结果。
要找到X和Y的按位AND,用二进制写入它们;然后,如果X和Y的相应位都是1,则二进制结果中的一位有1,否则为0。在大多数编程语言中,X和Y的按位AND写为X& Y。
例如: 旧机器生成数字7 = 0111。 新机器生成数字11 = 1011。 中奖号码为(7和11)=(0111和1011)= 0011 = 3.
通过这一措施,彩票公司希望减少欺诈性索赔的案件,但不幸的是,彩票公司的一名员工泄露了以下信息:旧机器将始终生成小于A的非负整数和新的将始终生成小于B的非负整数。
Catalina想要赢得这个彩票并试一试,她决定购买低于K的所有非负整数。
考虑到A,B和K,Catalina想知道机器可以通过多少种不同的方式生成一对能让她成为赢家的数字。
对于小输入,我们可以检查所有可能的对,但如何使用大输入。我想我们首先将二进制数表示为字符串,然后检查排列,这将使答案小于K
。但我似乎无法弄清楚如何计算2个二进制字符串的可能排列。
答案 0 :(得分:10)
我使用了一般的DP技术,我在很多细节in another answer中进行了描述。
我们想要计算对(a,b),使得< A,b< B和a& b< ķ。
第一步是将数字转换为二进制,并通过添加前导零将它们填充到相同的大小。我只是将它们填充到40的固定大小。想法是逐步建立有效的a和b。
设 f(i,loA,loB,loK)是大小为40-i的a和b的有效后缀对的数量。如果loA为真,则意味着直到i的前缀已经严格小于A的相应前缀。在这种情况下,a的下一个可能位没有限制。如果loA为假,则A [i]是我们可以放在当前前缀末尾的下一位的上限。 loB和loK有类似的含义。
现在我们进行了以下转换:
long long f(int i, bool loA, bool loB, bool loK) {
// TODO add memoization
if (i == 40)
return loA && loB && loK;
int hiA = loA ? 1: A[i]-'0'; // upper bound on the next bit in a
int hiB = loB ? 1: B[i]-'0'; // upper bound on the next bit in b
int hiK = loK ? 1: K[i]-'0'; // upper bound on the next bit in a & b
long long res = 0;
for (int a = 0; a <= hiA; ++a)
for (int b = 0; b <= hiB; ++b) {
int k = a & b;
if (k > hiK) continue;
res += f(i+1, loA || a < A[i]-'0',
loB || b < B[i]-'0',
loK || k < K[i]-'0');
}
return res;
}
结果是 f(0,false,false,false)。
如果添加了memoization,运行时为 O(max(log A,log B)),以确保每个子问题只解决一次。
答案 1 :(得分:1)
我所做的只是确定答案何时是A * B. 否则,只需暴力破解其余部分,此代码就会传递大量输入。
// for each test cases
long count = 0;
if ((K > A) || (K > B)) {
count = A * B;
continue; // print count and go to the next test case
}
count = A * B - (A-K) * (B-K);
for (int i = K; i < A; i++) {
for (int j = K; j < B; j++) {
if ((i&j) < K) count++;
}
}
我希望这有帮助!
答案 2 :(得分:-1)
整个答案是。
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <map>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
#define MAX_SIZE 32
int A, B, K;
int arr_a[MAX_SIZE];
int arr_b[MAX_SIZE];
int arr_k[MAX_SIZE];
bool flag [MAX_SIZE][2][2][2];
long long matrix[MAX_SIZE][2][2][2];
long long
get_result();
int main(int argc, char *argv[])
{
int case_amount = 0;
cin >> case_amount;
for (int i = 0; i < case_amount; ++i)
{
const long long result = get_result();
cout << "Case #" << 1 + i << ": " << result << endl;
}
return 0;
}
long long
dp(const int h,
const bool can_A_choose_1,
const bool can_B_choose_1,
const bool can_K_choose_1)
{
if (MAX_SIZE == h)
return can_A_choose_1 && can_B_choose_1 && can_K_choose_1;
if (flag[h][can_A_choose_1][can_B_choose_1][can_K_choose_1])
return matrix[h][can_A_choose_1][can_B_choose_1][can_K_choose_1];
int cnt_A_max = arr_a[h];
int cnt_B_max = arr_b[h];
int cnt_K_max = arr_k[h];
if (can_A_choose_1)
cnt_A_max = 1;
if (can_B_choose_1)
cnt_B_max = 1;
if (can_K_choose_1)
cnt_K_max = 1;
long long res = 0;
for (int i = 0; i <= cnt_A_max; ++i)
{
for (int j = 0; j <= cnt_B_max; ++j)
{
int k = i & j;
if (k > cnt_K_max)
continue;
res += dp(h + 1,
can_A_choose_1 || (i < cnt_A_max),
can_B_choose_1 || (j < cnt_B_max),
can_K_choose_1 || (k < cnt_K_max));
}
}
flag[h][can_A_choose_1][can_B_choose_1][can_K_choose_1] = true;
matrix[h][can_A_choose_1][can_B_choose_1][can_K_choose_1] = res;
return res;
}
long long
get_result()
{
cin >> A >> B >> K;
memset(arr_a, 0, sizeof(arr_a));
memset(arr_b, 0, sizeof(arr_b));
memset(arr_k, 0, sizeof(arr_k));
memset(flag, 0, sizeof(flag));
memset(matrix, 0, sizeof(matrix));
int i = 31;
while (i >= 1)
{
arr_a[i] = A % 2;
A /= 2;
arr_b[i] = B % 2;
B /= 2;
arr_k[i] = K % 2;
K /= 2;
i--;
}
return dp(1, 0, 0, 0);
}