题解 P2317 [HNOI2005] 星际贸易
题解 P2317 [HNOI2005] 星际贸易
题意
先化简,然后变成了这个样子:
Coke 依次经过 \(1\) ~ \(n\) 这几个位置,每个位置离起点距离为 \(L_i\) ,最后停靠在第 \(n\) 个位置结束。
在每一个位置可以通过付出 \(A_i\) 的代价获取 \(B_i\) 的价值,他总共可以承担 \(m\) 的代价。
在离开和到达某个位置时,都要使用 \(1\) 点能量,在每个位置都有可能可以补充能量,价格是 \(P_i\),一开始,总共有 \(R\) 点能量。
同时在不管哪个位置停靠,都要在这个位置进行维护,费用为 \(F_i\),不维护最多可以走的距离是 \(L_0\)。
在满足价值最大的同时,他希望花在能量与维护上的总价最少。
其中,输入数据满足:一定有 ,并使得只有—种获得最大价值的方法。
分析
再通过简化的题意发现,最大价值与能量与维护都无关,所以直接使用一个01背包就可以解决:
设 \(f_{i,j}\) 表示:到第 \(i\) 个位置,花费 \(j\) 的代价,最大总价值为多少。然后就是最基础的01背包。
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
int n,m;
int a[N],b[N];
int f[N][N];
RCL(f,-1,f,1),f[0][0]=0;
FOR(i,1,n)FOR(j,0,m){
f[i][j]=f[i-1][j];
if(j>=a[i])tomax(f[i][j],f[i-1][j-a[i]]+b[i]);
}
再看题目中有一个十分明显的提示:
输入数据满足:一定有 ,并使得只有—种获得最大价值的方法。
那么我们找到最大值,然后反向寻找一下就能知道那几个位置是做过买卖的,也就是必经的位置:
int n,m,id1,id2;
int a[N];
int f[N][N];
bool chosen[N];
FOR(i,1,m)if(f[n][i]>f[n][id1])id1=i;
id2=id1;
DOR(i,n,1)if(f[i][id2]!=f[i-1][id2])id2-=a[i],chosen[i]=1;
然后既然知道哪些点是必经的,我们就可以把能量与维护的费用独立出来进行一个 DP:
设 \(g_{i,j}\) 表示:到第 \(i\) 个位置,剩余 \(j\) 的能量,最小总价为多少。
那么列出转移方程式:
由此可以解出 \(O(n^4)\) 的算法,但是有一个明显的化简,我们把二维寻找转换为一维:
然后可以得到一个 \(O(n^3)\) 的的做法,不过直接枚举还不够,我们用一种简单的数据结构优化:单调队列。
同时注意到,总共用的能量不会超过 \(2n\) ,那么就可以在输入时就把他化掉。
那么再结合上面的细节即可打出代码,(码风清奇,请见谅):
struct Deque{
#define Pii pair<int,int>
#define F first
#define S second
deque< Pii > dq;
Deque(){Clear();}
inline void Clear(){while(!dq.empty())dq.pop_back();}
inline void Push(Pii x){
while(!dq.empty()&&dq.back().S>=x.S)dq.pop_back();
dq.emplace_back(x);
}
inline void Update(int x){while(!dq.empty()&&dq.front().F<x)dq.pop_front();}
inline int Query(){return dq.empty()?INF:dq.front().S;}
#undef Pii
#undef F
#undef S
}dq[N<<1];
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
int n,R,L;
int l[N],P[N],F[N];
int g[N][N<<1];
tomin(R,(n<<1));
RCL(g,INF,g,1),g[0][R]=0;
dq[R].Push({0,0});
FOR(i,1,n)FOR(j,0,R){
if(P[i]&&j)tomin(g[i][j],g[i][j-1]+P[i]);
dq[j+2].Update(l[i]-L),tomin(g[i][j],dq[j+2].Query()+F[i]);
if(chosen[i])dq[j].Clear();
dq[j].Push({l[i],g[i][j]});
}
紧接着,找到最小值:
int R,id1,id2;
int g[N][N<<1];
FOR(i,0,R)if(g[n][i]<g[n][id2])id2=i;
输出:
int n,id1,id2;
int f[N][N],g[N][N<<1];
cout<<f[n][id1]<<" "<<f[n][id1]-g[n][id2]<<endl;
同时,题目中还有不成立的情况,那么加入特判:
int n,L;
int l[N];
FOR(i,n,1)if(l[i]-l[i-1]>L)return (cout<<"Poor Coke!"<<endl),0;
输出也要更改为:
int n,id1,id2;
int f[N][N],g[N][N<<1];
if(g[n][id2]>=INF)cout<<"Poor Coke!"<<endl;
else cout<<f[n][id1]<<" "<<f[n][id1]-g[n][id2]<<endl;
完整代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=2e3+10;
int n,m,R,L,id1,id2;
int a[N],b[N],l[N],P[N],F[N];
int f[N][N],g[N][N<<1];
bool chosen[N];
struct Deque{
#define Pii pair<int,int>
#define F first
#define S second
deque< Pii > dq;
Deque(){Clear();}
inline void Clear(){while(!dq.empty())dq.pop_back();}
inline void Push(Pii x){
while(!dq.empty()&&dq.back().S>=x.S)dq.pop_back();
dq.emplace_back(x);
}
inline void Update(int x){while(!dq.empty()&&dq.front().F<x)dq.pop_front();}
inline int Query(){return dq.empty()?INF:dq.front().S;}
#undef Pii
#undef F
#undef S
}dq[N<<1];
signed main(){
cin>>n>>m>>R>>L;tomin(R,(n<<1));
FOR(i,1,n)cin>>a[i]>>b[i]>>l[i]>>P[i]>>F[i];
FOR(i,n,1)if(l[i]-l[i-1]>L)return (cout<<"Poor Coke!"<<endl),0;
RCL(f,-1,f,1),f[0][0]=0;
FOR(i,1,n)FOR(j,0,m){
f[i][j]=f[i-1][j];
if(j>=a[i])tomax(f[i][j],f[i-1][j-a[i]]+b[i]);
}
FOR(i,1,m)if(f[n][i]>f[n][id1])id1=i;
id2=id1;
DOR(i,n,1)if(f[i][id2]!=f[i-1][id2])id2-=a[i],chosen[i]=1;
RCL(g,INF,g,1),g[0][R]=0;
dq[R].Push({0,0});
FOR(i,1,n)FOR(j,0,R){
if(P[i]&&j)tomin(g[i][j],g[i][j-1]+P[i]);
dq[j+2].Update(l[i]-L),tomin(g[i][j],dq[j+2].Query()+F[i]);
if(chosen[i])dq[j].Clear();
dq[j].Push({l[i],g[i][j]});
}
id2=0;
FOR(i,0,R)if(g[n][i]<g[n][id2])id2=i;
if(g[n][id2]>=INF)cout<<"Poor Coke!"<<endl;
else cout<<f[n][id1]<<" "<<f[n][id1]-g[n][id2]<<endl;
return 0;
}
时间复杂度 \(O(n^2)\),足够通过。

浙公网安备 33010602011771号