SSL1377 竞赛真理


原题链接

外网进不去

题目大意

n n n类题目,每一类题目有切掉他所需的时间和切掉他可以得到的分数,每一类题目都有无限多个,现在给你有限的时间,求出在有限的时间内你可以得到最多多少分。
S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input 1 \mathbf{1} 1

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

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output 1 \mathbf{1} 1

50

S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input 2 \mathbf{2} 2

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

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output 2 \mathbf{2} 2

70

解题思路

本题有两种方法,第一种是 d f s dfs dfs,另一种是 d p dp dp
d f s dfs dfs
可以用 d f s dfs dfs裸搜,但是这样肯定是会Alt
的,所以我们需要剪枝

  • 可行性剪枝

如果说不够时间,或者深度大过 n n n则退出。
这是最基本的剪枝,对于本题来说没什么大用,所以你会发现:后面的几个大数据点,Alt
了,程序还需要进一步的优化。

  • 最优性剪枝

对于每一道题,肯定都有他自己的效率,将效率求出来,即:
效 率 = m a x ( 全 做 分 数 全 做 时 间 , 骗 分 分 数 骗 分 时 间 ) 效率=max(\frac{\tiny{全做分数}}{\tiny{全做时间}},\frac{\tiny{骗分分数}}{\tiny{骗分时间}}) =max(,)
把题目按照效率从大到小排序,随后就出来了第二个剪枝:
如果说当前题目的效率 × \times ×剩余时间 ≤ \le 当前最多分数的话,那么因为后面的效率都 < < <当前效率,所以后面题目所得的分数肯定比现在要低,加上之后依然不能 > > >当前最多分数,所以直接退出。
上代码!

#include <stdio.h>
#include <algorithm>
using namespace std;
struct data
{
	data(){}
	data(int a,int b,int c,int d,double e)
	{
		v1=a,w1=b,v2=c,w2=d;
		p=e;
	}
    int v1,w1,v2,w2;
    double p;
};
data t[40];
int n,m,ans=0;
bool pd(data x,data y){return x.p>y.p;}
void dfs(int dep,int sum,int left)
{
    if (left<0) return;
    if (sum>ans) ans=sum;
    if (dep>n) return;
    if (sum+t[dep].p*left<ans) return;
    dfs(dep+1,sum+t[dep].v1,left-t[dep].w1);
    dfs(dep+1,sum+t[dep].v2,left-t[dep].w2);
    dfs(dep+1,sum,left);
}
int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    {
        int v1,w1,v2,w2;
        cin>>v1>>w1>>v2>>w2;
        t[i]=data(v1,w1,v2,w2,max(v1/(w1*1.0),v2/(w2*1.0)));
    }
    sort(t+1,t+n+1,cmp);
    dfs(1,0,m);
    printf("%d\n",ans);
    return 0;
}

d p dp dp
d p dp dp来做的话,这个其实就是一个改进版的 01 01 01背包,而每一题的两种做法,只需要取最大值就可以了,即:
d p j dp_{j} dpj为时间为 j j j是可以取得的最大分数, w 1 i w1_i w1i w 2 i w2_i w2i为全做和骗分所需要的时间, c 1 i c1_i c1i c 2 i c2_i c2i为全做和骗分可以拿到的分数。
d p j = max ⁡ ( d p j , [ j ≥ w 1 i ] ( d p j − w 1 i + c 1 i ) , [ j ≥ w 2 i ] ( d p j − w 2 i + c 2 i ) ) dp_{j}=\max(dp_j,[j\geq w1_i](dp_{j-w1_i}+c1_i),[j\geq w2_i](dp_{j-w2_i}+c2_i)) dpj=max(dpj,[jw1i](dpjw1i+c1i),[jw2i](dpjw2i+c2i))

上代码

#include<iostream>
using namespace std;

int n,m;
int w1[40],c1[40],w2[40],c2[40];
int dp[1080010];

int main()
{
    cin>>n>>m;
    for(int i=1; i<=n; i++) cin>>c1[i]>>w1[i]>>c2[i]>>w2[i];
    for(int i=1; i<=n; i++)
    {
        for(int j=m; j>=1; j--)
        {
            if(j>=w1[i]) dp[j]=std::max(dp[j],dp[j-w1[i]]+c1[i]);
            if(j>=w2[i]) dp[j]=std::max(dp[j],dp[j-w2[i]]+c2[i]);
        }
    }
    cout<<dp[m]<<endl;
    return 0;
}

完美切题~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值