vijos 1153 背包+标记

 

 

描述

新一年度的猫狗大战通过SC(星际争霸)这款经典的游戏来较量,野猫和飞狗这对冤家为此已经准备好久了,为了使战争更有难度和戏剧性,双方约定只能选择Terran(人族)并且只能造机枪兵。

比赛开始了,很快,野猫已经攒足几队机枪兵,试探性的发动进攻;然而,飞狗的机枪兵个数也已经不少了。野猫和飞狗的兵在飞狗的家门口相遇了,于是,便有一场腥风血雨和阵阵惨叫声。由于是在飞狗的家门口,飞狗的兵补给会很快,野猫看敌不过,决定撤退。这时飞狗的兵力也不足够多,所以没追出来。

由于不允许造医生,机枪兵没办法补血。受伤的兵只好忍了。555-
现在,野猫又攒足了足够的兵力,决定发起第二次进攻。为了使这次进攻给狗狗造成更大的打击,野猫决定把现有的兵分成两部分,从两路进攻。由于有些兵在第一次战斗中受伤了,为了使两部分的兵实力平均些,分的规则是这样的:1)两部分兵的个数最多只能差一个;2)每部分兵的血值总和必须要尽可能接近。现在请你编写一个程序,给定野猫现在有的兵的个数以及每个兵的血格值,求出野猫按上述规则分成两部分后每部分兵的血值总和。

 

格式

输入格式

第一行为一个整数n(1<=n<=200),表示野猫现在有的机枪兵的个数。以下的n行每行一个整数,表示每个机枪兵的血格(1<=ai<=40)。

输出格式

只有一行,包含两个数,即野猫的每部分兵的血值总和,较小的一个值放在前面,两个数用空格分隔。

 

样例1

样例输入1

 
3
35
20
32

样例输出1

 
35 52

 

限制

各个测试点1s

 

提示

TO 狗狗:这道题的数据范围我已经尽量按星际的游戏规则来了,如果你再固执于由于机枪兵的攻击力一定使不能达到某些血格值或者游戏中一定要造农民不能使机枪兵的人数达到200的话,我只能决定将那场猫狗大战的录像公开于世人了!!!

 

题意:将一堆东西分成两堆,时两堆差值尽可能小,且数量相差不大于1

思路:如果没有数量差1的要求,我们可以用01背包的思路来做,将背包容量缩成1/2,而在这种条件下的最优显然不可行。需要转换思路,dp[i][j]代表这堆的当前血量为i,已经取了j个是否可行。这样一来,我们在最后找到差值最小的数就可以了。

 

/** @Date    : 2016-11-27-19.54
  * @Author  : Lweleth (SoungEarlf@gmail.com)
  * @Link    : https://github.com/
  * @Version :
  */
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <utility>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <stack>
#include <queue>
//#include<bits/stdc++.h>
#define LL long long
#define PII pair<int ,int>
#define MP(x, y) make_pair((x),(y))
#define fi first
#define se second
#define PB(x) push_back((x))
#define MMG(x) memset((x), -1,sizeof(x))
#define MMF(x) memset((x),0,sizeof(x))
#define MMI(x) memset((x), INF, sizeof(x))
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5+2000;


int dp[8020][205];
int a[500];
int main()
{
    int n;
    while(cin >> n)
    {
        MMF(dp);
        int sum = 0;
        for(int i = 1; i <= n; i++)
            scanf("%d", a + i), sum += a[i];

        int s = (n + 1) / 2;
        //sum /= 2;
        dp[0][0] = 1;
        for(int i = 1; i <= n; i++)
        {
            for(int k = s; k >= 0; k--)
            {
                for(int j = sum; j >= 0; j--)
                {
                    if(j + a[i] <= sum && dp[j][k])
                        dp[j+a[i]][k+1] = 1;
                }
            }
        }
        int det = INF;
        int ans = 0;
        for(int i = 0; i <= 40 * s; i++)
        {
            int t = (sum - i) - i;
            if(t < 0)
                t = -t;
            if(dp[i][s] && t < det)
            {
                det = t;
                if(i <= sum / 2)
                    ans = i;
                else
                    ans = sum - i;
            }
        }
        printf("%d %d\n", ans, sum - ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/Yumesenya/p/6109055.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值