题意
必须满足辩方总分D和控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的 |D-P| 值相同,那么选辩控双方总分之和D+P最大的方案即可。
思路
dp(j, k)表示,取j 个候选人,使其辩控差为k 的所有方案中,辩控和最大的那个方案的辩控和。
综上:dp[j][k]=dp[j-1][k-V[i]]+S[i]
开始傻了,写成了dp[i][x] = dp[k][x-1] + w[i]
存储了每次最小的差,又开了一个数组存最大,但单纯的差只需要贪心就能求最小了,所以dp应该存的是每个差所对应的最大和正向计算,输出的时候就用正向的输出了,注意要按照编号排序
这里对绝对值的处理也很巧妙,通过fix的值来左右两边计算dp的方程写不对真的很困扰
代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 206;
const int INF = 0x7f7f7f7f;
int n, m;
int dp[ 21 ][ 805 ]; // dp[j][k]:取j个候选人,使其辩控差为k的所有方案中,辩控和最大的方案
int path[ 21 ][ 805 ]; //记录所选定的候选人的编号
//前j个,差,下一个,差的数组
//确认dp[j][k]方案是否曾选择过候选人i
bool select ( int j, int k, int i, int v[] ) {
while ( j > 0 && path[ j ][ k ] != i )
k -= v[ path[ j-- ][ k ] ];
// j > 0代表i被选过,返回false
return j ? false : true;
}
int main () {
int kse = 1;
while ( cin >> n >> m && n ) {
int *p = new int[ n + 1 ];
int *d = new int[ n + 1 ];
int *s = new int[ n + 1 ]; //和
int *v = new int[ n + 1 ]; //差
memset ( dp, -1, sizeof ( dp ) );
memset ( path, 0, sizeof ( path ) );
// INPt
for ( int i = 1; i <= n; ++i ) {
cin >> p[ i ] >> d[ i ];
s[ i ] = p[ i ] + d[ i ];
v[ i ] = p[ i ] - d[ i ]; //取绝对值,怎么减都行啦
}
int fix = m * 20; //总修正值,修正极限为从[-400,400]映射到[0,800]
// DP
dp[ 0 ][ fix ] = 0;
for ( int j = 1; j <= m; ++j ) // j个候选人
for ( int k = 0; k <= 2 * fix; ++k ) //差为k
if ( dp[ j - 1 ][ k ] >= 0 ) //初始化成了-1,所以这里代表上一个值存在
for ( int i = 1; i <= n; ++i ) //找下一个
if ( dp[ j ][ k + v[ i ] ] < dp[ j - 1 ][ k ] + s[ i ] )
if ( select ( j - 1, k, i, v ) ) {
//正向推导,方便输出,不方便查找所以需要每次都会回去判断所选的是否被选过
dp[ j ][ k + v[ i ] ] = dp[ j - 1 ][ k ] + s[ i ];
path[ j ][ k + v[ i ] ] = i;
}
// 取两边的差(绝对值)最小的那个(最接近fix)
int k;
for ( k = 0; k <= fix; ++k )
if ( dp[ m ][ fix - k ] >= 0 || dp[ m ][ fix + k ] >= 0 )
break;
int div = dp[ m ][ fix - k ] > dp[ m ][ fix + k ] ? ( fix - k ) : ( fix + k );
cout << "Jury #" << kse++ << endl;
cout << "Best jury has value ";
cout << ( dp[ m ][ div ] + div - fix ) / 2 << " for prosecution and value ";
cout << ( dp[ m ][ div ] - div + fix ) / 2 << " for defence:" << endl;
int *id = new int[ m ];
for ( int i = 0, j = m, k = div; i < m; ++i ) {
id[ i ] = path[ j-- ][ k ];
k -= v[ id[ i ] ];
}
sort ( id, id + m ); //可能忘记排序了
for ( int i = 0; i < m; ++i )
cout << ' ' << id[ i ];
cout << endl << endl;
}
return 0;
}