IBM Ponder this 2018 August

r_64 posted @ 2018年9月04日 11:55 in 未分类 , 58 阅读

每天只能刷刷水题娱乐自己的大脑。。。

先翻译题面。。有$18$个数$W_{1\dots 9}$和$G_{1\dots 9}$,它们互不相同且其中至少$17$个是质数,且$\sum_{i=1}^9W_i=\sum_{i=1}^9G_i$。令$W=\sum W_i/10$,则每个人喝到$W$的水,即$\frac{G_i}{W_i-W}=\frac{G_j}{W_j-W}$。设$\frac{G_i}{W_i-W}=k$,则$\sum G_i=k\sum W_i-9kW=k\sum W_i/10$。所以$k=10$。也就是$G_i=10W_i-\sum_jW_j$。

枚举$\sum_jW_j=S$,然后求出所有的$G\le S$使得$(G+S)/10$是整数,且$G$和$(G+S)/10$中至少一个是质数。然后dp:$f[i][j][k][l]$表示考虑最小的$i$个$G$,从中选$j$个,和为$k$,$l$表示已选的数中有没有$G$或$(G+S)/10$不是质数的情况。对于一个$S$判定的复杂度是$O(S^2)$的。这个方法只能保证$G$中的数两两不同,并不能保证不出现$G_i=W_j$的情况。但是对绝大部分解肯定都有$G_i\ne W_j$。

贴代码

#include <bits/stdc++.h>
using namespace std;

#define sz 4040
int good[sz], len_good, state[sz];
int isprime[sz];
int N = 4000;
int u = 9;
#define GOOD 1
#define OK 2
#define EVIL 3

void init_prime() {
  for (int i = 2; i <= N; i++) isprime[i] = 1;
  for (int i = 2; i <= N; i++)
    if (isprime[i])
      for (int j = i + i; j <= N; j += i) isprime[j] = 0;
}

void init(int S) {
  len_good = 0;
  for (int G = 1; G <= S; G++)
    if ((G + S) % (u + 1) == 0) {
      int W = (G + S) / (u + 1);
      if (isprime[G] || isprime[W]) {
        good[++len_good] = G;
        if (isprime[G] && isprime[W]) state[G] = GOOD;
        else state[G] = OK;
      } else state[G] = EVIL;
    }
}

int dp[sz][10][sz][2];
void doit(int S) {
  for (int i = 0; i <= len_good; i++)
    for (int j = 0; j <= u; j++)
      for (int k = 0; k <= S; k++)
        dp[i][j][k][0] = dp[i][j][k][1] = 0;

  dp[0][0][0][0] = 1;
  for (int i = 0; i < len_good; i++)
    for (int j = 0; j <= u; j++)
      for (int k = 0; k <= S; k++) {
        if (dp[i][j][k][0]) {
          dp[i + 1][j][k][0] = 1;
          if (k + good[i + 1] <= S)
            dp[i + 1][j + 1][k + good[i + 1]][state[good[i + 1]] == OK] = 1;
        }
        if (dp[i][j][k][1]) {
          dp[i + 1][j][k][1] = 1;
          if (k + good[i + 1] <= S && state[good[i + 1]] == GOOD)
            dp[i + 1][j + 1][k + good[i + 1]][1] = 1;
        }
      }
}

int G[11];
bool answer(int S) {
  int i = len_good, j = u, k = S, l = 0;
  if (!dp[i][j][k][l]) l = 1;
  if (!dp[i][j][k][l]) return false;
  printf("%d ", l);
  for (; i; i--)
    if (k >= good[i] && (l || state[good[i]] == GOOD))
      if (dp[i - 1][j - 1][k - good[i]][l - (state[good[i]] == OK)])
        l -= (state[good[i]] == OK), k -= (G[j--] = good[i]);
  printf("G=[");
  for (i = 1; i <= u; i++) printf("%d, ", G[i]);
  printf("],W=[");
  for (i = 1; i <= u; i++) printf("%d, ", (G[i] + S) / (u + 1));
  printf("]\n");
  return true;
}

int main() {
  init_prime();
  for (int S = 1; S <= sz; S++) {
    if (S % 1000 == 0) fprintf(stderr, "S=%d\n", S);
    init(S);
    if (len_good >= 8) {doit(S); answer(S);}
  }
  return 0;
}

 

import numpy as np
def prime(x) : return x != 1 and (not any([x % i == 0 for i in range(2, x)]))
def chk(w, g) :
	s = sum(w)
	print "sum =", s
	if s != sum(g): return 1
	if len(w) != 9 or len(g) != 9: return 2
	if len(np.unique(w + g)) != 18: return 3
	t = sum([prime(i) for i in w] + [prime(i) for i in g])
	if t < 17: return 4
	for i in range(9) :
		if w[i] * 10 - s != g[i]: return 5 + i
	print "info:", t, "primes"
	return 0

G = [3, 23, 43, 83, 103, 223, 383, 503, 523]
W = [189, 191, 193, 197, 199, 211, 227, 239, 241]
print chk(W,G)

G = [13, 53, 73, 113, 173, 193, 293, 353, 953]
W = [223, 227, 229, 233, 239, 241, 251, 257, 317]
print chk(W,G)

ps. 官方解法中发现了一个有趣的东西:我们可以暴力地(利用Green-Tao Theorem)将一个不满足素数条件的解变成满足素数条件的解。令$W=[45,46,47,48,49,50,51,52,58]$,$G=[4,14,24,34,44,54,64,74,134]$,则$W,G$满足条件。令$p_1,p_2,\dots,p_{134}$为长度为$131$的素数等差数列(Green-Tao Theorem:这种等差数列存在),则$W'=[p_{45},p_{46},p_{47},p_{48},p_{49},p_{50},p_{51},p_{52},p_{58}]$,$G'=[p_4,p_{14},p_{24},p_{34},p_{44},p_{54},p_{64},p_{74},p_{134}]$满足条件且$18$个数都是素数。解法来自Kipp Johnson


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter