题意:
就是小A会借自行车,给你m个a和b,在第a天借b次。sum(b)<=1e5。然后借一次的价格是k,不过还有n<=500种会员卡,每个会员卡有d天时效,一共k次免费借车,花费c元。现在问你在满足小A借车的情况下花费最少是多少。
思考:
- 当时看完感觉不太好做,因为又陷入了那种思想,就是看到第a天借b次,并且一张会员卡有d天时效,k次免费。我怎么去用这些会员卡呢,可能这个卡用了可以对后面的有用,所以就傻逼的感觉不能写了。
- 虽然当时我看到sum(b)<=1e5,并且n一共就500个会员卡,可以两种循环,但是dp怎么dp呢?我感觉这不能dp,而且赛时过的也很少,感觉可能是牛逼算法?然后就没看了。
- 其实这题和之前做的蓝桥杯的一样:2022蓝桥杯国赛B组-费用报销。把天数都展开,放到数组里,这样就可以对天数进行dp了,反正每天用一次。然后转移的时候,枚举每个物品,看看dp[i]要从哪里转移,如果每次都暴力转移点j,这样复杂度太高了。由于dp的递增性,所以尽量选择越左边的越好,所以有单调性那么完全可以二分或者用个指针idx,如果二分复杂度会高,直接双指针就可以了。
- 不过有一点一直卡我的就是,我就想枚举物品,然后枚举m天去dp,反正我每个物品都去更新你一遍就可以了。但是WA24,我就想怎么会错呢。实际上,比如枚举第i个物品的时候,第j个物品还没用过,现在不能保证我枚举的双指针的dp是最小的那个,因为第j个物品还没用,所以此时i之前每个点的dp,都不是真正的dp值,因为后面还会变。所以必须先枚举天数,再枚举物品,让第i天的dp值确定下来,为后面的人去用的时候这个dp就固定了,就可以双指针单调走了。
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e6+10,M = 510;
int T,n,m,k;
int va[M],vb[M],vc[M];
int vd[N],cnt;
int idx[N],dp[N];
signed main()
{
IOS;
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>va[i]>>vb[i]>>vc[i];
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
while(b--) vd[++cnt] = a;
}
sort(vd+1,vd+1+cnt);
for(int i=1;i<=cnt;i++) dp[i] = inf;
for(int i=1;i<=cnt;i++)
{
dp[i] = min(dp[i],dp[i-1]+k);
for(int j=1;j<=n;j++)
{
while(idx[j]+1<i&&vd[idx[j]+1]<=vd[i]-va[j]) idx[j]++;
while(idx[j]+1<i&&i-(idx[j]+1)>=vb[j]) idx[j]++;
dp[i] = min(dp[i],dp[idx[j]]+vc[j]);
}
}
cout<<dp[cnt];
return 0;
}
总结:
多多思考一下,不要退却的太早,稍微转化转化,思考一下就是经典的问题。当你看到一些可以用的提示的时候,多多想想怎么用,比如可以循环n*m,如果这样循环的话,你该怎么确定m呢,对吧。这样推一推就推出来了。