洛谷P2392 kkksc03考前临时抱佛脚(01背包/搜索)

题目背景

kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。

题目描述

这次期末考试,kkksc03 需要考 444 科。因此要开始刷习题集,每科都有一个习题集,分别有 s1,s2,s3,s4s_1,s_2,s_3,s_4s1,s2,s3,s4 道题目,完成每道题目需要一些时间,可能不等(A1,A2,…,As1A_1,A_2,ldots,A_{s_1}A1,A2,,As1B1,B2,…,Bs2B_1,B_2,ldots,B_{s_2}B1,B2,,Bs2C1,C2,…,Cs3C_1,C_2,ldots,C_{s_3}C1,C2,,Cs3D1,D2,…,Ds4D_1,D_2,ldots,D_{s_4}D1,D2,,Ds4)。

kkksc03 有一个能力,他的左右两个大脑可以同时计算 222 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。

由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。

输入格式

本题包含 555 行数据:第 111 行,为四个正整数 s1,s2,s3,s4s_1,s_2,s_3,s_4s1,s2,s3,s4

222 行,为 A1,A2,…,As1A_1,A_2,ldots,A_{s_1}A1,A2,,As1s1s_1s1 个数,表示第一科习题集每道题目所消耗的时间。

333 行,为 B1,B2,…,Bs2B_1,B_2,ldots,B_{s_2}B1,B2,,Bs2s2s_2s2 个数。

444 行,为 C1,C2,…,Cs3C_1,C_2,ldots,C_{s_3}C1,C2,,Cs3s3s_3s3 个数。

555 行,为 D1,D2,…,Ds4D_1,D_2,ldots,D_{s_4}D1,D2,,Ds4s4s_4s4 个数,意思均同上。

输出格式

输出一行,为复习完毕最短时间。

输入输出样例

输入 #1
1 2 1 3		
5
4 3
6
2 4 3
输出 #1
20
首先可以看到这四个科目的计算方法是一样的,同样的方法处理四次即可。因为两个脑可以同时运算,所以最理想的状态就是尽可能使两边脑子别闲着,换句话说就是把一个科目的题目尽可能分成两堆使得这两堆的耗时尽可能相等,然后取较大值累加到答案。
一开始我想的是贪心,即排序后把当前任务往累计任务耗时少的那半个脑子里加。但是贪心是错的,考虑8 6 5 5 4这组数据,正解应该是14->贪心GG。然后再想,本质问题就是把一科的题尽可能分成相等的两半,这样的话其中一半肯定尽可能接近这科题目总耗时的1/2,转化成01背包问题,背包的体积就是这科题目总耗时的1/2。
#include <bits/stdc++.h>
using namespace std;
int s[5];
int ttime[5][22];
int dp[1205]; 
int sum[5];
int ans=0;
int main()
{
    scanf("%d%d%d%d",&s[1],&s[2],&s[3],&s[4]);
    int i,j,k;
    for(i=1;i<=4;i++)
    {
        int tot=0;
        for(j=1;j<=s[i];j++)
        {
            scanf("%d",&ttime[i][j]);
            tot+=ttime[i][j];
        }
        sum[i]=tot;
    }
    for(i=1;i<=4;i++)
    {
        memset(dp,0,sizeof(dp));
        int mmax=0;
        for(j=1;j<=s[i];j++)
        {
            for(k=sum[i]/2;k>=ttime[i][j];k--)
            {
                dp[k]=max(dp[k],dp[k-ttime[i][j]]+ttime[i][j]);
                mmax=max(mmax,dp[k]);
            }
         } 
         ans+=(sum[i]-mmax);
    }
    cout<<ans;
    return 0;
}


原文地址:https://www.cnblogs.com/lipoicyclic/p/12488805.html