题目链接:http://codeforces.com/gym/102798/problem/L
题意:给一个b,要求将b分成任意n个数ti,使得(k%t1,k%t2,…,k%tn)的组合数最多。
解题思路:
(k%t1,k%t2,…,k%tn)的组合数可以发现是t1.t2…tn的最小公倍数,因为只有k经过最小公倍数次,才能会到最开始的(0,0,0,…,0)状态。所以这个题目就变成了求最小公倍数最大的题目。
要求最小公倍数最大,那么就要求充分利用b内的数,要求拆分后的数两两互质,如果a与b不互质,那么就会浪费一个a和b的最大公因子,比如a=3,b=9的时候,3和9同时出现的话,那么产生的影响也只有9,3不会起作用。
所以这里就用到了分组背包的思想,将不互质(互斥)的放在同一组背包中,然后进行分组背包。
PS:这题需要直接预处理30000范围的情况,否则会超时
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<map>
#include<vector>
#include<stdlib.h>
#include<math.h>
#include<string.h>
using namespace std;
const int MAXN = 3e4+10;
int t;
int b;
double dp[31000];
double lg[31000];
int prime[MAXN + 1];
void getPrime() {
memset(prime, 0, sizeof(prime));
for (int i = 2; i <= MAXN; i++) {
if (!prime[i])prime[++prime[0]] = i;
for (int j = 1; j <= prime[0] && prime[j] <= MAXN / i; j++) {
prime[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
}
}
}
void init() {
getPrime();
for (int i = 1; i <= MAXN; i++) {
lg[i] = log(1.0 * i);
dp[i] = 0;
}
dp[0] = 0;
for (int i = 1; i <= prime[0]; i++) {
for (int j = 30000; j >= prime[i]; j--) {
for (int k = prime[i]; k <= 30000; k*=prime[i]) {
if (j >= k) {
dp[j] = max(dp[j], dp[j - k]+lg[k]);
}
}
}
}
}
int main() {
init();
cin >> t;
while (t--) {
cin >> b;
printf("%.6f\n",dp[b]);
}
return 0;
}