陪审团题解

陪审团题解_第1张图片
状 态 表 示 : f [ i ] [ j ] [ k ] : 从 前 i 个 人 中 挑 选 了 j 个 人 , 且 对 应 的 辩 方 总 分 和 控 方 总 分 的 差 值 为 k , 的 最 大 总 分 状态表示:f[i][j][k]:从前i个人中挑选了j个人,且对应的辩方总分和控方总分的差值为k,的最大总分 :f[i][j][k]:ijk,

状 态 转 移 方 程 : 状态转移方程: :
f [ i , j , k ] = { f [ i − 1 ] [ j ] [ k ] ( 不 选 择 i ) f [ i − 1 , j − 1 , k − ( d [ i ] − p [ i ] ) ] ( 选 择 了 i ) f[i,j,k]=\left\{ \begin{matrix} f[i-1][j][k](不选择i) \\ f[i-1,j-1,k-(d[i]-p[i])](选择了i) \end{matrix} \right. f[i,j,k]={f[i1][j][k](i)f[i1,j1,k(d[i]p[i])](i)

**容易错的点:**预处理的时候要把所有 j = = 1 j==1 j==1的情况处理出来,这种情况比较好处理。
**为什么需要单独处理?**比如下图
陪审团题解_第2张图片
我 们 在 进 行 d p 的 时 候 , 要 访 问 到 我们在进行dp的时候,要访问到 dp访j-1 , 也 就 是 0 是 有 可 能 被 访 问 的 , 但 是 选 择 0 个 是 没 被 定 义 的 , 因 此 需 要 单 独 处 理 。 ,也就是0是有可能被访问的,但是选择0个是没被定义的,因此需要单独处理。 ,0访0

我 们 还 需 要 考 虑 是 否 能 够 进 行 f [ i , j , k ] = f [ i − 1 , j , k ] 什 么 时 候 不 能 进 行 这 步 呢 ? 当 根 本 无 法 从 前 i − 1 个 人 中 无 法 取 出 j 个 人 的 时 候 我们还需要考虑是否能够进行f[i,j,k]=f[i-1,j,k] 什么时候不能进行这步呢?\\ 当根本无法从前i-1个人中无法取出j个人的时候 f[i,j,k]=f[i1,j,k]i1j

因此我们需要增加一个p_num数组保存每一步的dp都取了多少个人
遍历公差的时候,还要判断是否这个人能够添加进去,也就是k-sub_val是否合法
陪审团题解_第3张图片

最 后 找 到 解 决 方 案 : 我 们 先 看 一 下 递 推 的 公 式 最后找到解决方案:我们先看一下递推的公式 :
f [ i , j , k ] = { f [ i − 1 , j , k ] ( 选 择 了 i ) f [ i − 1 , j − 1 , k − ( d [ i ] − p [ i ] ) ] + p [ i ] + d [ i ] ( 没 有 选 择 i ) f[i,j,k]=\left\{\begin {matrix} f[i-1,j,k](选择了i) \\ f[i-1,j-1,k-(d[i]-p[i])]+p[i]+d[i](没有选择i) \end{matrix} \right. f[i,j,k]={f[i1,j,k](i)f[i1,j1,k(d[i]p[i])]+p[i]+d[i](i)
如 果 f [ n , m , f i n a l k ] ! = f [ n , m , f i n a l k ] , 说 明 了 取 到 了 第 n 个 如果f[n,m,final_k]!=f[n,m,final _ k],说明了取到了第n个 f[n,m,finalk]!=f[n,m,finalk]n
因 此 我 们 倒 过 来 遍 历 就 行 , 注 意 k 和 j 的 更 新 因此我们倒过来遍历就行,注意k和j的更新 kj

对 了 , 在 初 始 化 数 组 的 时 候 , 不 要 和 合 法 的 数 组 值 冲 突 , 否 则 很 难 辨 认 。 比 如 最 后 一 次 提 交 的 时 候 我 w a 了 一 发 , 因 为 我 把 其 中 两 个 数 组 初 始 化 为 0 , 在 输 入 的 值 为 全 0 的 时 候 就 会 错 误 , 改 成 m e m s e t − 1 就 好 了 对了,在初始化数组的时候,不要和合法的数组值冲突,否则很难辨认。\\比如最后一次提交的时候我wa了一发,因为我把其中两个数组初始化为0\\,在输入的值为全0的时候就会错误,改成memset -1 就好了 wa00memset1

#include 
using namespace std;
#define int long long
const int N = 200 + 5;
int p[N];
int d[N];
int f[N][22][800 + 5];
int p_num[N][22][800 + 5];
int n, m;
int base = 400;
int start;
int score_d;
int score_p;
int final_k;
stack<int>id;

void init()
{
    score_p = 0;
    score_d = 0;
    final_k = 0;
    while (!id.empty())
    {
        id.pop();
    }
    memset(p, 0, sizeof p);
    memset(d, 0, sizeof d);
    memset(f, -1, sizeof f);
    memset(p_num, 0, sizeof p_num);

}
int getIndex(int val)
{
    return val + 400;
}
int validSub(int val)
{
    return val >= -400 && val <= 400;
}

void print()
{
    cout << "Jury #" << ++start << endl;
    cout << "Best jury has value " << score_p << " for prosecution and value " << score_d << " for defence:" << endl;

    while (!id.empty())
    {
        cout << " ";
        cout << id.top();
        id.pop();
    }
    cout <<"\n\n";
}


signed main()
{
    while (cin >> n >> m, n || m) {
        init();

        for (int i = 1; i <= n; i++) {
            cin >> p[i] >> d[i];
        }


        for (int i = 1; i <= n; i++)
        {
            for (int k = -400; k <= 400; k++)
            {
                int sub = d[i] - p[i];

                int index = getIndex(k);
                f[i][1][index] = f[i - 1][1][index];
                p_num[i][1][index] = p_num[i - 1][1][index];

                if (sub == k && d[i] + p[i] > f[i][1][index])
                {
                    f[i][1][index] = d[i] + p[i];
                    p_num[i][1][index] = 1;
                }

            }
        }


        for (int i = 2; i <= n; i++) {
            for (int j = 2; j <= min(i, m); j++) {
                for (int k = -400; k <= 400; k++) {
                    if (i == 3 && j == 2 && k == -2) {
                        cout << "";
                    }

                    int index = getIndex(k);

                    if (p_num[i - 1][j][index] == j) {
                        f[i][j][index] = f[i - 1][j][index];//不选i
                        p_num[i][j][index] = p_num[i - 1][j][index];//不选i
                    }

                    int sub_val = d[i] - p[i];
                    if (validSub(k - sub_val))//选i
                    {

                        if (p_num[i - 1][j - 1][getIndex(k - sub_val)] == j - 1 &&
                            f[i - 1][j - 1][getIndex(k - sub_val)] + p[i] + d[i] > f[i][j][index])
                        {
                            f[i][j][index] = f[i - 1][j - 1][getIndex(k - sub_val)] + p[i] + d[i];
                            p_num[i][j][index] = p_num[i - 1][j - 1][getIndex(k - sub_val)] + 1;
                        }
                    }
                }
            }
        }

        for (int sub = 0; sub <= 400; sub++) {
            int index1 = base + sub;
            int index2 = base - sub;


            if (p_num[n][m][index1] == m || p_num[n][m][index2] == m)
            {
                if (p_num[n][m][index1] == m && p_num[n][m][index2] == m)
                {
                    final_k = f[n][m][index1] > f[n][m][index2] ? sub : -sub;
                }
                else
                {
                    final_k = ((p_num[n][m][index1] == m) ? sub : -sub);
                }
                break;
            }
        }
//        cout << final_k;



        int choose_num = m;
        int temp_k = final_k;
        for (int i = n; i; i--)
        {
            int index = getIndex(temp_k);
            if (f[i][choose_num][index] != f[i - 1][choose_num][index])
            {
                score_p += p[i];
                score_d += d[i];
                id.push(i);
                choose_num--;
                temp_k -= (d[i] - p[i]);
            }
        }

        print();
    }
    return 0;

}

你可能感兴趣的:(背包问题,动态规划,c++,算法)