您的位置:首页 > 运维架构

TopCoder SRM DIV2 Level 3: RelativelyPrimeSubset

2013-06-30 20:56 441 查看
题目: http://community.topcoder.com/stat?c=problem_statement&pm=12074&rd=14739
题目大意: 给定n个正整数,从其中选取k个数使得集合中所有数互素。 求k的最大值。

例子: {2,3,7,11,4}, 最大互素集合为{2,3,7,11} 和 {3,7,11,4},所以集合个数为4。

范围: 1<=n<=50, 集合中元素最大值不超过100.

求解:第一反应这是个典型的0-1背包问题,采用动态规划求解. dp[i, mask]表示第i个元素时, 集合中素数的位信息为mask时,集合的大小。

递推式: dp[i, mask] = max {dp[i-1, mask'] + 1, dp[i-1, mask]} (s[i]符合mask条件), 其中mask' = mask ^ GetMask(s[i]); 如果s[i]与mask不符合, 表示s[i]的某个素数因子在mask中位信息为1,但mask在这一位为0 ,这种mask情况下, 第i个元素不能被选中, dp[i, mask] = dp[i-1, mask].

初始值, s[0]=p1^e1*p2^e2*,,,*pk^ek, 则dp[0, e1e2...ek] = 1, 其余都为0.

时间复杂度分析, 俨然该算法的时间复杂度为O(n*2^k), 100以内的素数有25个,则该算法时间复杂度为O(50 * 2^25), 2^25 大概在千万级别(25*log2 = 7.5),时间复杂度略高。

后来看解题报告才知,这里有个小技巧: 如果元素大于50且为素数,那么它必定在最大互素集合中。why? 因为该素数要么就是自己, 它组成的合数必然大于100,是不会在集合中出现的。 那么可以将k减小至小于50的素数个数, 总共有15个。时间复杂度将为O(50*2^15)。不超过6位数。

C# Code

using System;
using System.Collections.Generic;
using System.Text;

class RelativelyPrimeSubset
{
public int findSize(int[] S)
{
List<int> primes = new List<int>();
bool[] isPrimes = new bool[101];
for (int i = 2; i <= 100; i++)
{
if (IsPrime(i))
{
primes.Add(i);
isPrimes[i] = true;
}
else
{
isPrimes[i] = false;
}
}
List<int> list = new List<int>();
int cnt = 0;
foreach(int a in S)
{
if (isPrimes[a] && a > 50)
{
cnt++;
}
else
{
list.Add(a);
}
}
S = list.ToArray();
int n = S.Length;
int m = (1 << 16) + 1;
int[,] dp = new int[101, m];

for (int i = 0; i < m; i++)
{
dp[0, i] = 0;
}
dp[0, GetMask(primes, S[0])] = 1;
int mask_max = 1 << 16;
for (int i = 1; i < n; i++)
{
int x = S[i];
int x_mask = GetMask(primes, x);
for (int mask = 0; mask < mask_max; mask++)
{
if (IsValidation(mask, x_mask))
{
int c = mask & (~x_mask);
int a = dp[i - 1, mask];
if (dp[i - 1, c] + 1 > a)
{
a = dp[i-1, c] + 1;
}
dp[i, mask] = a;

}
else
{
dp[i, mask] = dp[i-1, mask];
}

}
}
int max = 0;
for(int mask = 0; mask <m; mask ++)
{
if (dp[n-1, mask] > max)
{
max= dp[n-1, mask];
}
}
return max + cnt;
}

// mask1 contain mask2
private static bool IsValidation(int mask1, int mask2)
{
int x = mask1;
int y = mask2;

while (true)
{
int p1 = x % 2;
int p2 = y % 2;
if (p1 == 0 && p2 == 1)
{
return false;
}
x = x / 2;
y = y / 2;
if (x == 0 || y == 0)
{
break;
}
}
if (x == 0 && y > 0)
{
return false;
}
return true;
}

private static int GetMask(List<int> primes, int x)
{
int mask = 0;

for (int i = 0; i < primes.Count; i++)
{
if (x % primes[i] == 0)
{
mask |= (1 << i);
x = x / primes[i];
}

}
return mask;
}
private static bool IsPrime(int n)
{
if (n == 2)
{
return true;
}

for (int i = 2; i * i <= n; i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}

}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息