题意:
给定三个整数a,n,m,
有一个数轴,现在你要从点0向点a走,一开始你在点0处
有n个区间下雨了,第i个区间[L,R]表示点L到点R之间下雨
有m个点上有雨伞,第i个点x有权值p,表示x位置有一个重量为p的伞,一个位置可能有多把伞
你只能从0开始向右走
你可以捡起和携带任意数量的伞,也可以丢弃身上的任意数量的伞
没走过1单位距离,你的疲劳值增加你身上伞的总重量
要想走过下雨的地方,必须身上要有伞
现在问从0到达a的最少疲劳值是多少
如果0无法到达a,就输出-1
解法:
首先这题的区间是格子,而点是格点,
因此给定的区间要转化为左闭右开(原题里样例1可以看出)
如果最左边的伞位置大于最左边的雨位置
那么肯定无解,否则一定有解,这个结论是显然的
每次身上只带一把伞一定是最优的,这个结论也是显然的
令d[i]表示到达点i位置的最小疲劳值
is[i]表示点i是否有雨(前面已经把格子区间已经转化为格点区间了)
has[i]表示位置i的伞的重量,如果有多把伞则取min,has[i]=0说明没伞
转移方程:
如果is[i]==0,那么d[i]=d[i-1],
否则d[i]=min{d[j]+has[j]*(i-j)},其中0<=j<i,且has[j]!=0,即j位置有伞
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e3+5;
int is[maxm];
int has[maxm];
int d[maxm];
int a,n,m;
signed main(){
cin>>a>>n>>m;
int ismi=1e9;//记录最左边的雨位置
int hasmi=1e9;//记录最左边的伞位置
for(int i=1;i<=n;i++){
int l,r;cin>>l>>r;//区间是左开右闭的
is[l+1]++,is[r+1]--;//这里用了差分,直接遍历标记也行
ismi=min(ismi,l);
}
for(int i=1;i<=a;i++){
is[i]+=is[i-1];
}
for(int i=1;i<=m;i++){
int x,p;cin>>x>>p;
hasmi=min(hasmi,x);
if(has[x])has[x]=min(has[x],p);
else has[x]=p;
}
if(hasmi>ismi){//无解情况
cout<<-1<<endl;
return 0;
}
for(int i=1;i<=a;i++){
d[i]=1e18;
if(!is[i])d[i]=d[i-1];
else{
for(int j=0;j<i;j++){//枚举选择的伞的位置
if(has[j]){
d[i]=min(d[i],d[j]+has[j]*(i-j));
}
}
}
}
if(d[a]==1e18){
cout<<-1<<endl;
}else{
cout<<d[a]<<endl;
}
return 0;
}