小苯的粉丝关注

[题目链接](https://www.nowcoder.com/practice/697cf95e85cc4061b1d3429d39e00780)

思路

小苯有 个旧账号,第 个账号有 个粉丝。他要让新账号恰好获得 个粉丝。在第 个旧账号发一次推荐文章,新账号涨粉 ;他还可以最多选择一个旧账号多次发推荐文章,该账号涨粉 (不取半)。求最少在几个旧账号发文。

问题转化

选择一个账号子集 ,从中至多挑一个账号 获得 的贡献,其余账号 各贡献 。要求总贡献恰好等于 ,最小化

枚举 + 0-1 背包

由于 ,数据范围很小,可以枚举"哪个账号被选为多次发文的账号"(也可以不选),然后用 0-1 背包求剩余目标的最少物品数。

具体地,令 。枚举 表示不选任何账号多次发文, 表示选第 个账号):

  1. 计算剩余目标 。若 ,跳过。
  2. 上做 0-1 背包: 表示恰好凑出和为 所需的最少物品数。
  3. 有解,则答案候选为

取所有候选的最小值即为答案。若无解,输出

样例演示

输入 ,对应

  • (账号 3 多次发文,贡献 ),剩余目标
  • (去掉 )中凑出 ,选 ,用 个物品。
  • 总计 个账号,即为最优解。

复杂度分析

  • 时间复杂度:。枚举 轮,每轮做一次 的 0-1 背包。
  • 空间复杂度:,DP 数组大小。

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    vector<int> a(n);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);

    vector<int> b(n);
    for (int i = 0; i < n; i++) b[i] = a[i] / 2;

    int ans = INT_MAX;

    for (int j = -1; j < n; j++) {
        int target = k - (j >= 0 ? a[j] : 0);
        if (target < 0) continue;

        vector<int> dp(target + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 0; i < n; i++) {
            if (i == j || b[i] == 0) continue;
            for (int s = target; s >= b[i]; s--) {
                if (dp[s - b[i]] != INT_MAX) {
                    dp[s] = min(dp[s], dp[s - b[i]] + 1);
                }
            }
        }

        if (dp[target] != INT_MAX) {
            int cost = dp[target] + (j >= 0 ? 1 : 0);
            ans = min(ans, cost);
        }
    }

    printf("%d\n", ans == INT_MAX ? -1 : ans);
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), k = sc.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) a[i] = sc.nextInt();

        int[] b = new int[n];
        for (int i = 0; i < n; i++) b[i] = a[i] / 2;

        int ans = Integer.MAX_VALUE;

        for (int j = -1; j < n; j++) {
            int target = k - (j >= 0 ? a[j] : 0);
            if (target < 0) continue;

            int[] dp = new int[target + 1];
            Arrays.fill(dp, Integer.MAX_VALUE);
            dp[0] = 0;

            for (int i = 0; i < n; i++) {
                if (i == j || b[i] == 0) continue;
                for (int s = target; s >= b[i]; s--) {
                    if (dp[s - b[i]] != Integer.MAX_VALUE) {
                        dp[s] = Math.min(dp[s], dp[s - b[i]] + 1);
                    }
                }
            }

            if (dp[target] != Integer.MAX_VALUE) {
                int cost = dp[target] + (j >= 0 ? 1 : 0);
                ans = Math.min(ans, cost);
            }
        }

        System.out.println(ans == Integer.MAX_VALUE ? -1 : ans);
    }
}