题目链接:点我
题目大意:
个物品,每个物品体积为 ,花费为 ,每次选 个物品,使这三个物品 ,并且 ,求最小花费???
解题思路:
明显是dp,问题是怎么dp,在这里给出两种方法,因为要选三个物品,下面一种是特殊的方法,一种是一般的方法:
:
代表第
个物品作为选出的三个物品中的第
个物品的最小花费,那么只需要遍历一遍
,每次遍历取两个物品,在前i个物品中选取符合要求且花费最小的物品,那么状态转移方程就是:
,其中:
且
。
:因为只需要选三个物品,那么我先遍历一遍i,每次遍历中,找到i之后花费最小的那个物品,这样就选择了二个物品的最优解,记录到这个
中
再次遍历一遍i,每次遍历选择i之前花费最小的那个物品,这样既不会重复,也就选到了三个物品的最优解!
PS:第一种方法可以延伸到任意数量的物品(复杂度允许的话),而第二种方法只能选择三个物品,因为只能遍历两次~
代码思路:
状态转移: ,其中: 且 。
时间复杂度:
核心:熟练的使用dp,不行就多练吧弟弟
第一种方法(推荐):
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3007, inf = 0x3f3f3f3f;
int f[maxn][4], n, val[maxn], cost[maxn], ans = inf;
int main()
{
scanf("%d", &n);
for (int i=1; i<=n; i++) scanf("%d", val + i);
for (int i=1; i<=n; i++) scanf("%d", cost + i);
memset(f, 0x3f, sizeof(f));
for (int i=1; i<=n; i++) {
f[i][1] = cost[i];
for (int k=2; k<=3; k++)
for (int j=1; j<i; j++)
if (val[j] < val[i])
f[i][k] = min(f[i][k], f[j][k - 1] + cost[i]);
}
for (int i=1; i<=n; i++)
ans = min(ans, f[i][3]);
printf("%d\n", ans == inf ? -1 : ans);
return 0;
}
第二种方法(也推荐):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, dp[3005];
int s[3005], c[3005];
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", s+i);
for(int i=1; i<=n; i++) scanf("%d", c+i);
for(int i=1; i<=n; i++) dp[i] = INT_MAX;
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
if(s[i]<s[j])
dp[j] = min(dp[j], c[i]+c[j]);
int ans = INT_MAX;
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
if(s[i]<s[j] && dp[i]!=INT_MAX)
ans = min(ans, dp[i]+c[j]);
if(ans == INT_MAX) printf("-1");
else printf("%d", ans);
}