CF 13C Sequence
这是道双倍经验题,做完可以做Luogu P4597
01
\(\texttt{离散化+滚动数组}\)
Idea
\(dp[i][j]\)表示前i个数以\(a[j]\)为结尾的满足要求的最少的操作,可是题目给的最大数是\(10^9\),二维数组的\(j\)元素不可能开这么大,所以需要离散化一下,改成前i个数以第\(j\)个数为结尾的满足要求的最少的操作。
\(dp[i][j]=min(dp[i][j],\text{第i-1个位置前j个数的最小操作}+abs(b[j]-a[i]))\) \(b\)数组是原来输入的\(a\)数组排好序且离散化之后的数组
因为\(b\)数组是从小到大排好序的,所以在第\(i\)个位置时第\(j\)个数肯定比\(i-1\)个位置的前j个数要大。
最后\(dp[5000][5000]\)也不能存下,那么这个二维数组就要滚一下了。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<queue>
#define ll long long
#define maxn 100001
#define mod 998244353
#define eps 1e-6
#define pi acos(-1.0)
#define de(x) ((x)*(x))
using namespace std;
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
const ll inf=1ll<<62;
ll dp[2][5007],a[5007],b[5007];
int main(){
ll n=read();
for(ll i=1;i<=n;i++){
a[i]=read();
b[i]=a[i];
}
sort(b+1,b+n+1);
ll tot=unique(b+1,b+n+1)-b-1;//离散化
dp[1][1]=inf;
for(ll i=1;i<=tot;i++){
dp[1&1][i]=fabs(b[i]-a[1]);
dp[2&1][i]=inf;
}
for(ll i=2;i<=n;i++){
ll minn=inf;
for(ll j=1;j<=tot;j++){
minn=min(minn,dp[(i-1)&1][j]);//取上一个位置中前j颗不同高度的树中花费最小
dp[i&1][j]=min(dp[i&1][j],minn+abs(b[j]-a[i]));//当前位置等于上一个位置中前j颗树花费最小+这个位置是第j颗树的花费
}
for(ll j=1;j<=tot;j++) dp[(i+1)&1][j]=inf;
}
ll ans=inf;
for(ll i=1;i<=tot;i++) ans=min(ans,dp[n&1][i]);//取最后一个位置是第j颗树是花费最小
printf("%lld",ans);
return 0;
}
02
考虑\(DP\)
Idea
设\(dp[i][j]\)表示前\(i\)个已经符合要求,而且最大数不大于原序列第\(j\)个元素最小需要的代价。
那么我们可以得出转移方程\(dp[i][j]=min(dp[i][j−1],dp[i−1][j]+abs(a[i]−c[j])\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<queue>
#define ll long long
#define maxn 100001
#define mod 998244353
#define eps 1e-6
#define pi acos(-1.0)
#define de(x) ((x)*(x))
using namespace std;
inline ll read(){
ll x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
const ll inf=1ll<<62;
ll ans=inf;
ll a[maxn],c[maxn],dp[2][maxn];
int main(){
int n; scanf("%d",&n);
for(int i=1;i<=n;i++) a[i]=read(),c[i]=a[i];
sort(&c[1],&c[n+1]);
memset(dp,127,sizeof dp);
for(int i=1;i<=n;i++) dp[0][i]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
dp[1][j]=min(dp[1][j-1],dp[0][j]+abs(a[i]-c[j]));
swap(dp[1],dp[0]);
}
for(int i=1;i<=n;i++) ans=min(ans,dp[0][i]);
printf("%I64d",ans);
return 0;
}
\(P.S:\text{这个方法过不了CF原网站的#8}\)
DP优化
排完序后发现,每个数的更换值都是最小的.
那么有\(dp\)方程如下
\[dp[j]=abs(a[i]-a[j]);
\]
\[dp[j]=min(dp[j],dp[j-1]);
\]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<queue>
#define ll long long
#define maxn 5005
#define inf 2147483647
#define mod 998244353
#define eps 1e-6
#define pi acos(-1.0)
#define de(x) ((x)*(x))
using namespace std;
inline ll read(){
ll x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
ll a[maxn],c[maxn],dp[maxn];
int main(){
int n; scanf("%d",&n);
for(int i=1;i<=n;i++) a[i]=read(),c[i]=a[i];
sort(c+1,c+n+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
dp[j]+=abs(a[i]-c[j]);
if(j>1) dp[j]=min(dp[j],dp[j-1]);
}
printf("%I64d",dp[n]);
return 0;
}//洛谷上用时4+ s,CF上用时342 ms
03
用堆写
证明请看(我不会严格证明)
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<queue>
#define ll long long
#define maxn 500005
#define inf 2147483647
#define mod 998244353
#define eps 1e-6
#define pi acos(-1.0)
#define de(x) ((x)*(x))
using namespace std;
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
priority_queue<int>q;
ll ans;
int main(){
int n=read();
for(int i=1;i<=n;i++){
int x=read();
q.push(x);
if(q.top()>x){
ans+=q.top()-x;
q.pop();
q.push(x);
}
}
printf("%lld",ans);
return 0;
}
不随波,追随梦;不逐流,攀耸峰。不卑,补我所失;不亢,胜我所向。