洛谷p2392考前临时抱佛脚(动态规划和搜索)

6 篇文章 0 订阅
2 篇文章 0 订阅

题目背景

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

题目描述

这次期末考试,kkksc03 需要考 4 科。因此要开始刷习题集,每科都有一个习题集,分别有s1,s2,s3,s4​ 道题目,完成每道题目需要一些时间,可能不等
(A1,A2,…,As1​​,B1,B2,…,Bs2,C1,C2,…,Cs3,D1,D2,​,…,Ds4​​)。kkksc03 有一个能力,他的左右两个大脑可以同时计算 2 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。

输入格式

本题包含 555 行数据:
第 1 行,为四个正整数 s1,s2,s3,s4
第 2 行,为 A1,A2,…,As1​​ 共s1​ 个数,表示第一科习题集每道题目所消耗的时间。
第 3 行,为 B1,B2,…,Bs2共 s2个数。
第 4 行,为 C1,C2,…,Cs3​​ 共 s3​ 个数。
第 5 行,为 D1,D2,…,Ds4​​ 共 s4​ 个数,意思均同上。

输出格式

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

输入样例

1 2 1 3		
5
4 3
6
2 4 3

输出样例

20

动态规划题解:

四个科目不能同时计算,所以是四个科目分别计算得到的时间值之和。对于每一个科目,计算方式相同,现在左右脑同时计算,问所用时间最少,那么可以分析得出,当左右脑的时间差最少时,把该科目题目算完所用时间最少。要得出该时间,对于每道题,都有选与不选两种状态,即0-1背包问题。总时间的一半相当于背包总空间,价值和消耗都是花费的时间。计算出四个科目的时间,再相加就可以了。

#include<bits/stdc++.h>
using namespace std;
int a[5];//存四种科目每个科目的题目数
int ans = 0;//存所求,即四种科目所用最少时间和
int p[30],dp[2005];//存每个科目每道题所用时间和动态规划存最短时间
int main()
{
 for (int i = 1; i <=4; ++i)
 {
  cin >> a[i];//初始化四种科目每个科目的题目数
 }
 for (int i = 1; i <= 4; ++i)
 {
  int sum = 0;//计算每个科目题目算完所用最少时间
  for (int j = 1; j <= a[i]; ++j)
  {
   cin >> p[j];//初始化每个科目每个题目使用的时间
   sum += p[j];//计算所有题目的时间和
  }
  for (int j = 1; j <= a[i]; ++j)//接下来是动态规划的步骤
  {
   for (int v = sum / 2; v >= p[j]; --v)
   {
    dp[v] = max(dp[v], dp[v - p[j]] + p[j]);
   }
  }
  ans += sum-dp[sum/2];//要加上另一半时间,因为计算出来的这半时间小于等于sum/2,所以要加上另一半时间
  memset(dp, 0, sizeof(dp));//对dp数组初始化为0
 }
 cout << ans;//输出所求
 return 0;
}

搜索题解

对于每一道题,都有放左脑和放右脑两种选择,可以进行搜索找符合条件的排列。搜索的结束条件是已计算题目个数大于该科目题目数,那么可以得出该科目计算完毕所需要的时间为左右脑使用时间最大值中的最小值。(最大值是对于一种情况中,左右脑的最大值,最小值是对于每种情况得出的最大值中的最小值,就是所求最小值)

#include<bits/stdc++.h>
using namespace std;
int Left, Right, minn, ans;//左脑,右脑,最小值,所求值
int s[5];//存每个科目的题目数
int a[21];//存
void search(int x,int y) {
    if (x > s[y]) 
    {
        minn = min(minn, max(Left, Right));
        return;
    }
    Left += a[x];//把此题加到左脑
    search(x + 1,y);//继续安排下一道题
    Left -= a[x];//把此题退出左脑
    Right += a[x];//把此题加到右脑
    search(x + 1, y);//继续安排下一道题
    Right -= a[x];//把此题退出右脑
}
int main() {
    cin >> s[1] >> s[2] >> s[3] >> s[4];
    for (int i = 1; i <= 4; ++i) //对于每一个科目做同样的操作
    {
        Left = Right = 0;
        minn = 19260817;
        for (int j = 1; j <= s[i]; ++j)
            cin >> a[j];//初始化第i个科目第j道题目要消耗的时间
        search(1,i);//从第i个科目的第一道题开始搜索
        ans += minn;
    }
    cout << ans;
    return 0;
}

注意:这道题不能用贪心算法做

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值