1 条题解
-
0
暴力部分
:
我们直接在每个位置,要么把油加满,要么把油加到可以去某一个后面的加油站的量,然后继续即可。其实不好想到。之所以给是因为原题范围就是。
:
表示走到,有的油的最小代价即可,复杂度。
:
我们转移的时候在同一层,当完全背包来转移即可。
memset(dp,-1,sizeof(dp)); for (int i=1;i<=C;i++) dp[1][i]=p[1]*i; for (int i=1;i<=n;i++) { for (int j=0;j<=C;j++) if (dp[i][j]!=-1) { MIN(dp[i][j+1],dp[i][j]+p[i]); } for (int j=x[i];j<=C;j++) if (dp[i][j]!=-1) { MIN(dp[i+1][j-x[i]],dp[i][j]); } } cout<<dp[n][x[n]]<<endl;
特殊性质
特殊性质A:就是原题的做法了。
特殊性质B:大概率每走步就会加油一次,并且要加满,表示走到加满油的最小代价,然后枚举下一次加油的位置,复杂度。
正解1
倒着贪心,从到,考虑每一个的决策。
具体一点:
我们假设一共个加油站,第个加油点可以加价格的油,接下来要跑的距离才能到下一个加油站。
我们考虑从后向前去看每一次决策。
只考虑第个加油站,我显然只需要购买的油就足够了。
所以我先记一下:我在号加油站,用的价格,购买了的油。
在第个加油站,首先,如果只有这么两个加油站,我肯定要在买的油。这时候,我还可以在这里,用的价格,买最多的油。
那么,这里买的油,就可以替代在处购买的油了,对不?
也就是说:如果在此处,比的决策划算,我就可以用的价格买油,来替代在买的油。
那么,算法的大框架就出来了:
假设当前我已经考虑了从到的所有决策,我当前的决策序列从左到右分别是。
那么,当有了以后,我首先需要以的价格在购买的油。然后,我还有的油可以在此处买,用来依次替代处的决策。
于是我直接用一个栈来维护这个决策即可,复杂度线性。
核心代码如下:
for (int i=n;i>=1;i--) { //ret:还剩多少可以用,tot:一共加了多少 ll ret=C-x[i],tot=0; //当栈不空,并且栈顶的价格比p[i]大 while (!S.empty() && ret!=0 && S.top().price>p[i]) { nod tmp=S.top(); S.pop(); ll num=min(ret,tmp.num); //算一下最多能替代多少 sum-=num*tmp.price; sum+=num*p[i]; //修改这次决策带来的影响 tmp.num-=num; tot+=num; ret-=num; if (tmp.num) S.push(tmp); } sum+=p[i]*x[i]; S.push(nod(p[i],tot+x[i])); }
完整代码如下:
#include<bits/stdc++.h> #define ll long long #define maxn 1000005 #define mod 1000000007 using namespace std; ll n,C; ll x[maxn],p[maxn]; stack<pair<ll,ll>> Q; int main() { cin>>n>>C; for (int i=1;i<=n;i++) cin>>x[i]; for (int i=1;i<=n;i++) cin>>p[i]; ll sum=0; pair<ll,ll> tt; for (int i=n;i>=1;i--) { sum+=p[i]*x[i]; ll ret=C-x[i]; while (ret && !Q.empty()) { tt=Q.top(); if (tt.first<=p[i]) break; Q.pop(); ll num=min(ret,tt.second); sum-=num*(tt.first-p[i]); tt.second-=num; ret-=num; if (tt.second) Q.push(tt); } Q.push({p[i],C-ret}); } cout<<sum<<endl; }
正解2
上述思路,正着也是可以考虑的。
我们对于第个加油站,认为:我可以在此处,以的价格,加的油。
那么,对于第一个加油站来说,我要加的油,才能跑到去,所以我此时,还剩下价格的的油。
对于第个加油站,我可以以的价格,加的油。
这时候我有这么两个决策:
(1)在加油站,以的价格,加的油。
(2)在加油站,以的价格,加的油。
这两个决策显然是矛盾的,因为如果我同时选的话,我怎么可能有那么大的油箱呢?
这个时候,我就需要考虑一下了:
假设,那么我就可以认为,我只剩下这么两个决策:
(1)在加油站,以的价格,加的油。
(2)在加油站,以的价格,加的油。
这样,即便两个加满,也只是。
假设,那么我就可以认为,我只剩下这么两个决策:
(1)在加油站,以的价格,加的油。
(2)在加油站,以的价格,加的油。
可以发现,无论哪种情况发生,我剩下的决策,一定满足下述两个特性:
(1)总加油量是。
(2)从左到右,价格依次递增。
那么我就可以动态的维护这个序列:
到了加油站后,首先把序列末尾,价格比大的那些决策都弹出来。
然后把这个决策,丢到队列的末尾去,保证丢进去的时候,队列内的总油量恰好是。
等维护完这个以后,我就从这个队列里面,贪心的从左到右拿出来的油,加到刚刚能去即可。
复杂度显然也是线性的。
#include<bits/stdc++.h> #define ll long long #define maxn 1000005 #define mod 1000000007 using namespace std; ll n,C; ll x[maxn],p[maxn]; pair<ll,ll> Q[maxn+maxn]; int head,tail; int main() { cin>>n>>C; for (int i=1;i<=n;i++) cin>>x[i]; for (int i=1;i<=n;i++) cin>>p[i]; head=1; tail=1; Q[head]={1ll<<30,C}; //为了保证队列里总大小是C ll sum=0; for (int i=1;i<=n;i++) { ll tt=0; //删掉末尾不如p[i]的决策 while (head<=tail && Q[tail].first>=p[i]) {tt+=Q[tail].second; tail--;} Q[++tail]={p[i],tt}; //在队列的开头拿出x[i]的油来加 ll ret=x[i]; while (ret) { ll num=min(ret,Q[head].second); sum+=Q[head].first*num; Q[head].second-=num; ret-=num; if (Q[head].second==0) head++; } Q[++tail]={1ll<<30,x[i]}; //为了保证队列里总大小是C } cout<<sum<<endl; }
信息
- ID
- 1423
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- (无)
- 递交数
- 130
- 已通过
- 20
- 上传者