dp-2019-2020 ICPC Southeastern European Regional Programming Contest (SEERC 2019)-B. Level Up

题目链接
题意:
有n件事,经验到a1时升第一级,经验到a2时升第二级
每个经验有x1,t1,x2,t2四个值
x1,t1分别表示升第一级前做这件事可得经验和所需时间
x2,t2分别表示升第一级后做这件事可得经验和所需时间
求升第二级时所需的最小时间
1≤n,a1,b1≤500
1≤x1,x2≤500
1≤t1,t2≤1e9
思路:
dp[j][k]表示升第一级前经验到j,升第一级后经验到k所需时间
dp[a1][a2]即为所求

对于第i件事:

升级前做这件事 dp[j+x1][k]=min(dp[j+x1][k],dp1[j][k]+t1)
这里需要注意两点
①如果j已经达到了a1,说明做这件事前已经升级,不能再在升级前做这件事了
②如果j+x1达到a1,则所处的经验需加到k上,且第一个括号里的最大值不能超过a1,第二个括号里的最大值不能超过k

升级后做这件事 dp[j][k+x2]=min(dp[j][k+x2],dp1[j][k]+t2)
这里需要注意一点
①如果k+x2达到a2,则第二个中括号里为min(a2,k+x2),即第二个括号里不能超过a2

★dp1为最终的dp值,同一件事转移的过程中,不能改变dp1的值,因为同一件事只能用一次,需要用做这件事前的dp值转移。
★初始化dp[j][k]=一个很大的值,dp[0][0]=0,dp1和dp相同;
★这i件事dp是需要先按x1从小到大排序,排序是为了让j和k较小是先有有效值(参照dp的式子想)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[505][505], dp1[505][505];
struct hh
{
    ll x1, t1, x2, t2;
} p[505];
bool cmp(hh p1, hh p2)
{
    return p1.x1 < p2.x1;
}
int main()
{
    ll n, a1, a2, i, j, k;
    scanf("%lld%lld%lld", &n, &a1, &a2);
    for (i = 0; i <= 504; i++)
        for (j = 0; j <= 504; j++)
            dp[i][j] = 1e15, dp1[i][j] = 1e15;
    for (i = 1; i <= n; i++)
        scanf("%lld%lld%lld%lld", &p[i].x1, &p[i].t1, &p[i].x2, &p[i].t2);
    sort(p + 1, p + n + 1, cmp); //按x1从小到大排序的目的是让j和k较小的先有值
    dp[0][0] = 0, dp1[0][0] = 0;
    for (i = 1; i <= n; i++)
    { //因为同一件事最多只能用1次,所以在同一件事dp的过程中不能改变原来的值,所以用dp记录,更新dp1,dp1是最终的dp值
        for (j = a1; j >= 0; j--)
        {
            for (k = a2; k >= 0; k--)
            {
                if (j == a1) //此时已经升级,所以只有升级后做这件事一种情况
                {
                    ll k1 = min(a2, k + p[i].x2);
                    dp[a1][k1] = min(dp[a1][k1], dp1[a1][k] + p[i].t2); //升级后
                }
                else //分升级前做这件事和升级后做这件事两种情况
                {
                    ll j1 = j + p[i].x1; //升级前做这件事后的经验
                    if (j1 <= a1)
                    {
                        dp[j1][k] = min(dp[j1][k], dp1[j][k] + p[i].t1); //升级前
                        ll k1 = min(a2, k + p[i].x2);
                        dp[j][k1] = min(dp[j][k1], dp1[j][k] + p[i].t2); //升级后
                    }
                    else
                    {
                        ll k1 = min(a2, k + j1 - a1);
                        dp[a1][k1] = min(dp[a1][k1], dp1[j][k] + p[i].t1); //升级前
                        ll k2 = min(a2, k + p[i].x2);
                        dp[j][k2] = min(dp[j][k2], dp1[j][k] + p[i].t2); //升级后
                    }
                }
            }
        }
        for (j = a1; j >= 0; j--)
            for (k = a2; k >= 0; k--)
                dp1[j][k] = dp[j][k];
    }
    if (dp1[a1][a2] > 0 && dp1[a1][a2] < 1e15)
        printf("%lld\n", dp1[a1][a2]);
    else
        printf("-1\n");
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值