题目链接
题意:
有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");
}