数値のセットがあり、その要素の任意の2つの要素の合計が整数割り切れないように最大サブセットを計算したいと考えています。この問題を解決しようとしましたが、効率的な応答ではない2次の解を見つけました。。ここで、は要素の数、は定数です。二次解より良いですか?K < 100 、N < 10000 N K
数値のセットがあり、その要素の任意の2つの要素の合計が整数割り切れないように最大サブセットを計算したいと考えています。この問題を解決しようとしましたが、効率的な応答ではない2次の解を見つけました。。ここで、は要素の数、は定数です。二次解より良いですか?K < 100 、N < 10000 N K
回答:
実際、これには線形時間アルゴリズムがあります。基本的な数論の概念のみを使用する必要があります。二つの数、所与の及びN 2、それらの和はに割り切れるKそれらの残りの合計が割り切れる場合のみ、K。言い換えると、
あなたが考慮する必要があることを第二のコンセプトはつまり、2つの数の和であるKそのうちの一つが、より厳密に小さくされていない場合にのみ、K / 2、その他は一切未満であるK / 2。言い換えると、
2つの数の和であれば、あなたが考慮する必要があることを第三の概念は、つまりあるKから、彼らの両方がずれる⌈ K / 2 ⌉ - 1特定することによりK ≤ ⌈ K / 2 ⌉、すなわち、
したがって、3番目の概念のevey 場合、r 1またはr 2のいずれかをソリューションセットに含める必要がありますが、両方を含めることはできません。実際にKで割り切れる数値の1つを入れることができます。Kが偶数の場合、余りがK / 2である数値を1つだけ追加できます。
したがって、ここにアルゴリズムがあります。
セット所与の、の溶液を設定見つけるせてSを、
アルゴリズムはかなり長いですが、アイデアは非常に簡単です。
すべての異なる自然数を含むサイズnのセットSを考えます。このセットから最大のサブセットを形成する必要があります。基本的な係数プロパティを使用し、いくつかの控除を追加して問題を解決します。皆さんのお役に立てれば幸いです。
任意の2つの自然数N1およびN2の場合:(N1 + N2)mod(K)=(R1 + R2)mod(K)ここで、R1 = N1modKおよびR2 = N2%K。1.(N1 + N2)modK = 0の場合、(R1 + R2)%K = 0を意味します。2.つまり、R1 + R2はK、2K、3Kのいずれかに等しくなければなりません。...3.しかし、R1は0とK-1の間にあり、R2も同じです。つまり、それらの合計はK-1 + K-1を超えることはできません= 2(K-1)。4. 2と3から、R1 + R2はKに等しい必要があると結論付けることができます。5. R1 + R2 = Kの場合、両方がK / 2に等しい必要があります(Kが偶数の場合のみ可能)またはそれらの1つはfloor [K / 2]より小さく、1つは同じより大きくなければなりません。6. R1 = TおよびR2 = KTと仮定します。残りがR1であるSから任意の数N1を、残りがR2であるSから任意の数N2を取得すると、それらの合計はKで割り切れます。残りがR1の数値、または残りがR2の数値。ただし、両方は含まれません。
ここで、インデックス0からK-1までのサイズKの配列Rを構築するとします。各インデックスの要素は、(Sで除算した)剰余がインデックス番号に等しいセットSの数を示します。合計がKで割り切れるので、余りが0の3つ以上の数は使用できません。したがって、カウンターをmin(R [0]、1)で初期化する必要があります。T = 1からT
C ++での同じアルゴリズムのコードは次のとおりです。
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n,k;
cin>>n>>k;
vector <int> a(n);
vector <int> r(k,0);
for(int i=0;i<n;i++)
{
cin>>a[i];
r[a[i]%k]++;
}
int ctr=min(1,r[0]);
for(int a=1;a<(k/2+1);a++)
{
if(a!=k-a)
ctr+=max(r[a],r[k-a]);
}
if(k%2==0&&r[k/2]!=0)
ctr++;
cout<<ctr;
return 0;
}
最初にサブセット配列のサイズのみをカウントするC#コードと、(ハッシュ)サブセット全体を含む別のコードに変換しようとしました。
カウント:
static int nonDivisibleSubset(int k, int[] S)
{
var r = new int[k];
for (int i = 0; i < S.Length; i++)
r[S[i] % k]++;
int count = Math.Min(1, r[0]);
if (k % 2 == 0 && r[k / 2] != 0)
count++;
for (int j = 1; j <= k / 2; j++)
{
if (j != k - j)
count += Math.Max(r[j], r[k - j]);
}
return count;
}
サブセットあり:
static int nonDivisibleSubset(int K, int[] S)
{
var r = new HashSet<int>();
var d = S.GroupBy(gb => gb % K).ToDictionary(Key => Key.Key, Value => Value.ToArray());
for (int j = 1; j <= K / 2; j++)
{
var c1 = d.GetValueOrDefault(j, new int[0]);
var c2 = d.GetValueOrDefault(K - j, new int[0]);
if (c1.Length == c2.Length) continue;
r.UnionWith(c1.Length > c2.Length ? c1 : c2);
}
if (d.ContainsKey(0))
r.Add(d[0].Max());
if (K % 2 == 0 && d.ContainsKey(K / 2))
r.Add(d[K / 2].Max());
return r.Count;
}