題目鏈接:Jury Compromise
問題描述:
控辯雙方選擇陪審團成員。每個陪審團成員對控辯雙方來說,都有一個數值表示合適程度(0 ~ 20)。現在要從n個待選陪審員中選出m位,要是這m個人對控辯雙方合適程度的差最小。如果有差相同的情況,那麼選擇對控辯雙方合適程度的和較大的那個組合。
主要思路:
1. 動態規劃
2. 記錄從 n 個人中選出 j 個,並且當前組合的控辯雙方合適程度差爲 k,且控辯雙方合適程度和爲 w,記錄形式爲:
f[j][k] = w
3. 如果已知 f[j][k] = w,並且有一個陪審員 i 尚未被選中
那麼就有 f[j+1][k+minus[i]] = w + plus[i]
這裏 minus[i] 和 plus[i] 表示第 i 個陪審員的控辯合適程度差和合適程度和。
4. 下面就是對路徑的記錄,這裏用 path[j][k] = i記錄 i j k 之間的連接關係。
個人體會:
感覺動態規劃的方法,其實並不神祕。只不過是在暴力破解的過程中,記錄下中間步驟的解,避免重複的計算。
源代碼:
#include <iostream>
#include <algorithm>
using namespace std;
int n, m;
int* minus;
int* plus;
int f[20+1][800+1];
int path[20+1][800+1];
bool available(int di, int dj, int dk)
{
while(dj)
{
if(path[dj][dk] == di)
{
return false;
}
else
{
dk = dk - minus[path[dj][dk]];
dj = dj - 1;
}
}
return true;
}
void DP()
{
memset(f, -1, sizeof(f));
memset(path, 0, sizeof(path));
f[0][0] = 0;
for(int j = 0; j < m; j++)
{
for(int k = 0; k < 40 * m; k++)
{
if(f[j][k] != -1)
{
for(int i = 0; i < n; i++)
{
if(available(i, j, k)) //判斷第 i 個陪審員是否已被選中
{
if(f[j+1][k+minus[i]] < f[j][k] + plus[i])
{
f[j+1][k+minus[i]] = f[j][k] + plus[i];
path[j+1][k+minus[i]] = i;
}
}
}
}
}
}
}
void Print()
{
int k = 20 * m;
int result = -1;
int step = 1;
if(f[m][k] != -1)
{
result = k;
}
else
{
while(true)
{
if((f[m][k+step] != -1) || (f[m][k-step] != -1))
{
result = f[m][k+step] > f[m][k-step] ? (k+step) : (k-step);
break;
}
step = step + 1;
}
}
int j = m;
int i = 0;
k = result;
int* resultpath = new int[m];
while(j)
{
resultpath[i] = path[j][k];
k = k - minus[path[j][k]];
j = j - 1;
i = i + 1;
}
int prosecution = 0, defence = 0;
for(i = 0; i < m; i++)
{
prosecution += minus[resultpath[i]] - 20 + plus[resultpath[i]];
defence += plus[resultpath[i]] - minus[resultpath[i]] + 20;
}
cout << "Best jury has value " << prosecution / 2 << " for prosecution and value " << defence / 2 << " for defence: " << endl;
sort(resultpath, resultpath + m);
for(i = 0; i < m; i++)
{
cout << resultpath[i] + 1 << " ";
}
cout << endl;
}
int main()
{
int index = 0;
int a, b;
while(true)
{
cin >> n >> m;
if(n == 0) break;
index++;
minus = new int[n];
plus = new int[n];
for(int i = 0; i < n; i++)
{
cin >> a >> b;
minus[i] = a - b + 20;
plus[i] = a + b;
}
DP();
cout << "Jury #" << index << endl;
Print();
}
return 0;
}