题目大意:
话说岛国有位博士,想要保护一个数据的安全,这个数是个质数,假设是k0。他的策略是这样的,首先他选择一个r,r>=1,和一个空集合C,然后选择r-1个不超过k0的质数,k1,k2,。。。,k(r-1),然后把r加到集合C中。第二步,对于每一个k0到k(r-1)。他要么直接把k放进集合,要么把k拆成几个数(随便拆,只要和等于k就行)然后把拆出来的数放进集合。最后一步,将集合里面所有的数按照非递减的顺序排列。
由于教授年纪有些大了,脑子不太好使,让你帮他个忙,通过最后的集合找出原来的k,对于饱经ACM训练的你来说,这还不是小菜一碟,一听老教授的要求,灵光一动,便老练的说到,“你这个有多种情况啊”,只见年迈的老教授眼中精光一闪,心想,“小伙子有几把刷子啊”,“这样吧,那你就找出你能恢复出来的最大质数好了”。
于是,善良的你,便不知不觉落进了猎人的圈套。。
思路:
首先,根据题目要求,集合中肯定有一个r,也就是最终质数的个数。那么首先枚举r,然后对于剩下的元素,由于元素个数最多只有14个,那么可以用二进制枚举的形式,找出能够组成的所有质数,判断这些质数对应二进制集合相互没有交集能否组成r个,并找出能组成的最大值就是最后答案。
对于每一个r,和所有剩下元素组成的质数集合,可以用dp来进行后续的判断。
dp[i][s]表示s所有二进制位为1那一位对应的数的集合组成i个质数后其中最大的质数。
那么,对于质数集合中的一个质数v,以及对应的二进制集合t,如果t与s没有交集,即s&t==0,那么,
dp[i+1][s|t] = max(dp[i+1][s|t],max(dp[i][s],v));
最后dp[r][(1<<n-1)-1] 就是当前r对应的质数最大值。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 200000
#define inf 0x3fffffff
int n;
int a[20],b[20];
bool fac[maxn];
int ans;
struct Node{
int val;
int s;
Node(){}
Node(int a,int b):val(a),s(b){}
};
vector<Node> vec;
int dp[20][maxn];
void init(){
memset(fac,false,sizeof(fac));
fac[0] = fac[1] = true;
for(int i=2;i<maxn;i++){
if(!fac[i]){
for(int j=i*2;j<maxn;j+=i){
fac[j] = true;
}
}
}
// for(int i=2;i<100;i++){
// if(!fac[i]) printf("%d\n",i);
// }
}
void check(int r){
for(int i=0;i<=r;i++){
for(int s=0;s<1<<(n-1);s++) dp[i][s] = 0;
}
for(int i=0;i<vec.size();i++) dp[1][vec[i].s] = vec[i].val;
for(int i=1;i<r;i++){
for(int s=0;s<1<<(n-1);s++){
if(dp[i][s]==0) continue; //当前状态无法拼出质数
for(int k=0;k<vec.size();k++){
if(s&vec[k].s) continue; //vec[i].s 存在元素已经被选择
int t = vec[k].s;
dp[i+1][s|t] = max(dp[i+1][s|t],max(vec[k].val,dp[i][s]));
}
}
}
ans = max(ans,dp[r][(1<<(n-1))-1]);
}
void solve(){
ans = 0;
for(int i=0;i<n && a[i]<n;i++){ // 枚举r
if(i>0 && a[i]==a[i-1]) continue; // 避免重复
memcpy(b,a,sizeof(a));
b[i] = inf; // b[i] is r
swap(b[i],b[n-1]);
vec.clear();
for(int s=0;s<1<<(n-1);s++){
int sum = 0;
for(int k=0;k<n-1;k++){
if(s&(1<<k)) sum += b[k]; //枚举所有能够拼出的质数 存放到vec中
}
if(!fac[sum]) {
vec.push_back(Node(sum,s));
//printf("r:%d sum:%d\n",a[i],sum);
}
}
check(a[i]);
//printf("xxxxxxxxxxxxxxx\n");
}
if(ans==0) printf("not a valid clue\n");
else printf("%d\n",ans);
}
int main(){
init();
int cnt=1;
while(scanf("%d",&n)){
if(n==-1) break;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
printf("Case %d: ",cnt++);
solve();
}
return 0;
}