刷题总结——竞赛得分(ssoj)

题目:

题目描述

ZZH 在经历了无数次学科竞赛的失败以后,得到了一个真理:做一题就要对一题!但是要完全正确地做对一题是要花很多时间(包括调试时间),而竞赛的时间有限。所以开始做题之前最好先认真审题,估计一下每一题如果要完全正确地做出来所需要的时间,然后选择一些有把握的题目先做。 当然,如果做完了预先选择的题目之后还有时间,但是这些时间又不足以完全解决一道题目,应该把其他的题目用贪心之类的算法随便做做,争取“骗”一点分数。根据每一题解题时间的估计值,确定一种做题方案(即哪些题目认真做,哪些题目“骗”分,哪些不做),使能在限定的时间内获得最高的得分。

输入格式

第一行有两个正整数 N 和 T,表示题目的总数以及竞赛的时限(单位秒)。以下的 N 行,每行 4 个正整数 W1i  、T1i 、W2i  、T2i ,分别表示第 i 题:完全正确做出来的得分、完全正确做出来所花费的时间(单位秒)、“骗”来的分数、“骗”分所花费的时间(单位秒)。

其中,3≤N≤30,2≤T≤1080000,1≤W1i ,W2i≤30000; 1≤T1i,T2i≤T。

输出格式

输出所能得到的最高分值。

样例数据 1

输入  [复制]

 

 

4 10800 
18 3600 3 1800 
22 4000 12 3000 
28 6000 0 3000 
32 8000 24 6000

输出

50

样例数据 2

输入  [复制]

 

 

3 7200 
50 5400 10 900 
50 7200 10 900 
50 5400 10 900

输出

70

题解:

  这里引用ssoi官方题解:

  简单动态规划,二维费用背包问题,需要使用滚动数组。

  此题几乎是裸的 0/1 背包。而 0/1 背包就是一个很典型的顺序可以交换的例子。当然前提是状态是 f[i][j]代表前 i 道题用 j 的时间得到的最多的分数。这种情况下你把 i 放在外层循环或把 j 放在外层循环都是可以 AC 的。因为状态转移方程是 f[i][j]=max(f[i-1][j-cost1[i]]+score1[i],f[i-1][j-cost2[i]]+score2[i],f[i-1][j])。这样无论循环是哪个在外面,枚举到当前状态时,所需的前驱状态都已经计算出来了。

  不过特别的,对于这道题,如果采取这样的状态,空间上有些紧张。所以我们必须把 i 那一维优化掉。注意到i每次是由 i-1 转移过来的,并且前驱状态的 j 一定小于等于当前状态的 j!所以如果不设 i 那一维,把 i 放在外面循环并且 j 循环用 downto 的话,就可以保证每次转移的前驱都是 i-1 的情况。这样时间复杂度不变依然是 O(NT),但空间复杂度降到了 O(T)。这是我们对这道题的特殊之处的特殊优化,也可以说我们是对 0/1 背包的特殊之处的特殊优化。

  注意用一位数组优化!注意循环外层先枚举题目,然后注意再开两个dp数组防止枚举到同一道题正解贪心都做的情况!!

  连基本的背包dp都做不来了····

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=35;
const int W=1080005;
int w1[N],v1[N],w2[N],v2[N],n,t;
int dp[W],dp1[W],dp2[W];
int main()
{
  //freopen("a.in","r",stdin);
  scanf("%d%d",&n,&t);
  for(int i=1;i<=n;i++)
    scanf("%d%d%d%d",&v1[i],&w1[i],&v2[i],&w2[i]);
  for(int i=1;i<=n;i++)
  {
    for(int j=t;j>=w1[i];j--)
      dp1[j]=max(dp1[j],dp[j-w1[i]]+v1[i]);
    for(int j=t;j>=w2[i];j--)
      dp2[j]=max(dp2[j],dp[j-w2[i]]+v2[i]);
    for(int j=1;j<=t;j++)
      dp[j]=max(dp1[j],dp2[j]);
  }
  int ans=0;
  for(int j=1;j<=t;j++)
    ans=max(ans,dp[j]);
  cout<<ans<<endl;
  return 0;
}

 

转载于:https://www.cnblogs.com/AseanA/p/7388084.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值