01背包;感谢ZCK大佬

题目描述

学校举行拔河比赛,所有的人被分成了两组,每个人必须(且只能够)在其中的一组,要求两个组的人数相差不能超过1,且两个组内的所有人体重加起来尽可能地接近。

输入

输入中的第一行只有一个整数n(1≤n≤100),第二行有n个以空格分隔的整数,表示每人的体重wi(所有的wi均满足:1≤wi≤450)。

输出

输出只有一行,该行有两个整数,以一个空格分隔。第一个数表示体重较大的组的体重总和,第二个数表示体重较小的组的体重总和。

题面看上去很短,题意也很好理解:就是将n个人分成两组,在人数差不大于1的情况下使它们差最小。

用cnt表示它们的体重之和,则是使两组重量之和尽可能接近cnt / 2。那么就是在cnt / 2的空间里尽可能地使选入的人重。乍一看跟“挤牛奶”没区别,都是两维的dp其中增加一位限制人数。i,j,k三维循环,

for (int i=; i<=n; i++)
for (int j=i; j>=; j--)
for (int k=cnt; k>=a[i]; k--)
因为每个人只能选一次,所以j,k逆序

i,j,k循环


先来讲讲我最早的想法:

这是一份80,一份90的代码

初始化是f[][] = 0

在这种dp[][]方程的定义下,f[i][j]表示1..i人,凑成0..j范围内能凑成的最大重量(我知道这非常奇怪。事实上,在请教ZCK之前我甚至以为这个f[i][j]表示i个人能否凑出j。所以我发现"当f[i][j] != 0时,f[i][j] != j "这个现象时候才开始怀疑我的dp方程)

f[j][k] = max(f[j][k], f[j-1][k-a[i]]+a[i]);

在最终选取ans的时候,由于我们希望ans最接近cnt / 2,那么循环是“cnt / 2 ... 1“的,则ans = max{f[n / 2][]}。在这种情况下,不能够只判断 f[n / 2][i] 。虽然这看上去很有道理——如果n是奇数,用cnt - f[n / 2][i]就是奇数那一队的人数嘛!ZCK解释说:“f[n / 2][] 和 f[n / 2+1][] (n为奇数)是两条不同的数轴,不能保证f[n / 2][]上最接近cnt / 2的值比f[n / 2+1][]上的更优。”由于我们枚举的f[n / 2][] ≤ cnt / 2,那么对应的f[n / 2+1][]就是 ≥ cnt / 2的。有可能n / 2+1人能凑出更接近cnt / 2的!

可能会想到:既然dp时候已经把f[][cnt ... 1]都表示出来了,那么只需要在寻找ans的时候改动一下,变成这样

for (int j=cnt; j>=1; j--)
{
if (abs(cnt - 2*f[n/2][j]) < abs(cnt-2*ans))ans = f[n/2][j]
}

实际上,这样的判断只有70分。

并不是因为寻找ans的过程有问题,是因为f[][]所表示的东西不是我们想要的


这原因是初始化。

memset(f,-127,sizeof(f));  将f初始化为一个很小很小的数,这样f[i][j]表示的东西就不一样了
f[0][0]=0;  必要的

这样改了之后,只需要"cnt ... 1"判f[n / 2][]即可.

当然"cnt / 2 ... 1"判f[n / 2][], f[n / 2+1][]也可

因为我的dp方程太凌乱了……为了阿掉这题爆交共30+……


ZCK:你的dp方程到底想表示什么???直接bool不就好了吗

bool f[103][45035];

实际上,这的确是最简洁而且dp方程表示最明确的一种方法……


讲了这么多,实际上只是想说明dp方程的 定义 和 初始化 对于dp来说有多么重要。短短的一句初始语句或者在脑海中闪过一瞬的dp方程碎片,就足以上演化爆零为AC的奇迹。一个max和一个if看上去没多大差别,事实上后续的处理可能失之千里。考虑问题要仔细全面,这绝对不是一句空话。

【dp】拔河比赛的更多相关文章

  1. codevs 1959 拔河比赛--判断背包内刚好装满n/2个物品

    1959 拔河比赛  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description 一个学校举行拔河比赛,所有的 ...

  2. codevs1959拔河比赛(二维费用背包)

    1959 拔河比赛  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold     题目描述 Description 一个学校举行拔河比赛,所有的人被分成了两组,每个人 ...

  3. CODEVS 1959 拔河比赛(另一版本)

    题目描述 Description 一个学校举行拔河比赛,所有的人被分成了两组,每个人必须(且只能够)在其中的一组,要求两个组的人数相差不能超过1,且两个组内的所有人体重加起来尽可能地接近. 输入描述 ...

  4. rqnoj71 拔河比赛

    题目描述 superwyh的学校要举行拔河比赛,为了在赛前锻炼大家,老师决定把班里所有人分为两拨,进行拔河因为为锻炼所以为了避免其中一方的实力过强老师决定以体重来划分队伍,尽 量保持两个队伍的体重差最 ...

  5. c 指针 及其位运算循环移动拔河比赛问题代码

    week_2_day1_7.7 周一//用字符数组 来实现 字母大小写转换#include<stdio.h>void desc( char *a ,int n){    char  *i ...

  6. [深搜]A. 【例题1】拔河比赛

    A . [ 例 题 1 ] 拔 河 比 赛 A. [例题1]拔河比赛 A.[例题1]拔河比赛 解析 模板题,选与不选 Code #include <bits/stdc++.h> #defi ...

  7. 【C/C++】拔河比赛/分组/招商银行

    题目:小Z组织训练营同学进行一次拔河比赛,要从n(2≤n≤60,000)个同学中选出两组同学参加(两组人数可能不同).对每组同学而言,如果人数超过1人,那么要求该组内的任意两个同学的体重之差的绝对值不 ...

  8. 【codevs1959】拔河比赛

    题目大意:给定一个有 N 个数的集合,将这 N 个数均分成两堆,求差值最小是多少. 题解:有关集合选数的问题,应该是背包问题,同时要求均分可知,选出的物品数目也应该是背包费用的一个维度,因此这是一个多 ...

  9. 【解题报告】[动态规划]RQNOJ - PID72 / 拔河比赛

    原题地址:http://www.rqnoj.cn/problem/72 解题思路:基本的01背包问题. 要求的就是在这些人中选出一些人,使得这些人的体重的和 不超过所有人的体重的一半 并最大. 代码: ...

随机推荐

  1. 百度搜索:有关Baiduspider的10个问题

    猫宁!!! 参考链接: http://help.baidu.com/question?prod_id=99&class=476&id=2996 https://ziyuan.baidu ...

  2. (十一)SpringBoot导出excel文件

    一:添加POI依赖 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-oox ...

  3. SimpleDateFormat线程不安全(转)

    有三种方法可以解决以上安全问题.  1).使用同步 package com.bijian.study.date; import java.text.ParseException; import jav ...

  4. 3.KPCR

    KPCR: CPU控制区(Processor Control Region) 当线程进入0环时, FS:[0]指向KPCR(3环时FS[0]-> TEB)每个CPU都有一个KPCR结构体(一个核 ...

  5. jq解析xml

    注意:url路径不能用相对路径,需要加入http协议

  6. 统计最长回文串(传说中的Manacher算法)Hihocoder 1032

    算法的核心就在这一句上了:p[i] = min(p[2*id-i], p[id] + id - i); #include <iostream> #include <cstdio> ...

  7. 爬虫的UA池和代理池

    爬虫的UA池和代理池   一.下载中间件 先祭出框架图: 下载中间件(Downloader Middlewares) 位于scrapy引擎和下载器之间的一层组件. - 作用: (1)引擎将请求传递给下 ...

  8. HDU6301(2018多校第一场)

    Bryce1010模板 http://acm.hdu.edu.cn/showproblem.php?pid=6301 队友AC的,没怎么看 #include<iostream> #incl ...

  9. c#学习系列之跳出循环 break,continue

    break是完全跳出循环: ) { ) break; Console.WriteLine(a++); } 此处输出1,2,3,4,5,6,7,8 continue是跳出当前的循环: ;i<=;i ...

  10. Coder(线段树)

    求一部分和的线段树,因为是对5取余,所以给定一段区间a-b,假设其位置会有变化,最多会有5种和,那么就可以保留这五种和,在用lz进行延迟标记时,保存位置变化了多少也就知道了该从当前和转到哪一个和. 当 ...