西南科技大学院赛 F题 上决╇ф的精确打击问题 【经典dp问题: 数组分割问题】

传送门
题意: 好像面试题就有这道题? 就是给定2*n个人, 每个人有一个值, 要你分成两堆, 并且要这两堆的值相加的差值要尽量的小, 问这个最小差值是多少.

思路: 还是很经典的一个dp, 那就是dp[i][j][k] 表示前i个人中选min(i, n)个人能否组合出 值为 k 的组法. 所以很明显对于当前这个值, 如果k-a[i] 是存在的, 那么就可以选上这个值就行一个组合, 否则不能选就只能继续继承上一个的状态. 所以转移方程是:
dp[i][j][k] = dp[i-][j][k];
if ( k >= a[i] && dp[i-1][j-1][k-a[i]) dp[i][j][k] = 1;

最后根据这个dp数组的含义找一遍就可以得到答案了….. 细节请看代码实现.

AC Code

const int maxn = 1e5+5;
bitset<505>dp[32][16];
int a[35];
void solve()
{
    int n;
    scanf("%d", &n); int sum = 0;
    dp[0][0][0] = 1;
    for (int i = 1 ; i <= 2*n ; i ++) {
        scanf("%d", &a[i]);
        sum += a[i];
        dp[i][0][0] = 1;
    }
    for (int i = 1 ; i <= 2*n ; i ++) {
        for (int j = 1 ; j <= min(i, n) ; j ++) {
            for (int k = 1 ; k <= sum/2 ; k ++) {
                dp[i][j][k] = dp[i-1][j][k];
                if (k >= a[i] && dp[i-1][j-1][k-a[i]]) {
                    dp[i][j][k] = 1;
                }
            }
        }
    }
    int ans = 0;
    for (int i = 1 ; i <= sum / 2 ; i ++) {
        if (dp[2*n][n][i]) ans = i;
    }
    printf("%d\n", abs(sum - 2*ans));
}

猜你喜欢

转载自blog.csdn.net/Anxdada/article/details/80043415