dp1
dp1
Max Sum Plus Plus
思路:
转移态方程$dp[i][j]=max(dp[i][j-1],dp[i][j-1]+a[j],dp[i-1][k]+a[j])$表示前j个数中恰好选i组的数的最大和。
意义依次为不取第j个数, 取第j个数归入当前组, 取第j个数归入新的组。同时改题还要用滚动数组优化一下。可以发现$dp[i-1][k]$就是上一组之前的最大dp,用一个数组存一下就可以了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <map>
#include <vector>
using namespace std;
const int maxn = 1e6+10;
int a[maxn];
int b[maxn];
int dp[maxn];
int main()
{
int m,n;
while(scanf("%d%d",&m,&n)!=EOF)
{
memset(dp,0,sizeof dp);
memset(b,0,sizeof b);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int tep;
int ans=0;
for(int i=1;i<=m;i++)
{
tep=-1e9;
for(int j=i;j<=n;j++)
{
dp[j]=max(dp[j-1],b[j-1])+a[j];
b[j-1]=tep;
tep=max(tep,dp[j]);
}
}
printf("%d\n",tep);
}
return 0;
}
Ignatius and the Princess IV
思路:???不知道这道题和dp有什么关系,但是出现在题单里。map存一下每个数出现次数即可。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <map>
#include <vector>
#include <unordered_map>
using namespace std;
const int maxn = 1e6+10;
int a[maxn];
int b[maxn];
int dp[maxn];
unordered_map <int ,int > ma;
int main()
{
int m,n;
while(scanf("%d",&n)!=EOF)
{
ma.clear();
int ans;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
ma[a[i]]++;
if(ma[a[i]]==(n+1)/2)
{
ans=a[i];
}
}
printf("%d\n",ans);
}
return 0;
}
Monkey and Banana
思路:
状态转移方程$dp[i]=max(dp[j]+a[i].z,dp[i])$
表示以第j块砖为基础时,能搭建的最高高度。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <map>
#include <vector>
#include <unordered_map>
using namespace std;
const int maxn = 1e6+10;
struct node{
int x,y,z;
};
bool cmp(node a,node b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
node a[maxn];
int n;
int dp[maxn];
int main()
{
int t=0;
while(~scanf("%d",&n),n)
{
int cnt=0;
for(int i=1;i<=n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[++cnt]={x,y,z};a[++cnt]={x,z,y};
a[++cnt]={y,x,z};a[++cnt]={y,z,x};
a[++cnt]={z,y,x};a[++cnt]={z,x,y};
}
sort(a+1,a+1+cnt,cmp);
int x,y;
int ans=0;
for(int i=1;i<=cnt;i++)
{
x=a[i].x;
y=a[i].y;
dp[i]=a[i].z;
for(int j=i-1;j>=1;j--)
{
if(a[j].x<x&&a[j].y<y)
{
dp[i]=max(dp[i],dp[j]+a[i].z);
ans=max(ans,dp[i]);
}
}
}
printf("Case %d: maximum height = %d\n",++t,ans);
}
}
Doing Homework
思路:
看到n<=15猜测是状压dp,同时要记录一下状态转移的过程,因为要输出字典序最小,需要倒着遍历。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <map>
#include <vector>
#include <unordered_map>
#include <stack>
using namespace std;
const int maxn = 16;
const int inf = 0x3f3f3f3f;
struct node{
string s;
int d,c;
};
bool cmp(node a,node b)
{
if(a.d==b.d) return a.c<b.c;
return a.d<b.d;
}
struct node1{
int fa,now,time,score;
};
int n;
node a[maxn];
node1 dp[1<<15];
vector <string > g;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
g.clear();
memset(dp,0,sizeof dp);
cin>>n;
for(int i=0;i<n;i++)
{
string s;
int d,c;
cin>>s>>d>>c;
a[i]={s,d,c};
}
for(int i=1;i<(1<<n);i++)
{
dp[i].score=inf;
for(int j=n-1;j>=0;j--)
{
int temp=1<<j;
if(temp&i)
{
int tem = i-temp;
int tt=dp[tem].time+a[j].c-a[j].d;
if(tt<0) tt=0;
if(tt+dp[tem].score<dp[i].score)
{
dp[i]={tem,j,dp[tem].time+a[j].c,(tt+dp[tem].score)};
}
}
}
}
cout<<dp[(1<<n)-1].score<<endl;
stack <int > st;
int p=(1<<n)-1;
while(dp[p].time)
{
st.push(dp[p].now);
p=dp[p].fa;
}
while(!st.empty())
{
int id=st.top();
st.pop();
cout<<a[id].s<<endl;
}
}
}
Super Jumping! Jumping! Jumping!
思路:
和上面的第三题很像,转移态方程$dp[i]=max(dp[i],a[j]+dp[j])$ $a[i]>a[j]$表示前i个并包括第i个的最大递增子序列和。用两层for循环遍历一遍
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#include <vector>
#include <map>
using namespace std;
const int maxn = 1e4+10;
int a[maxn];
int dp[maxn];
int main()
{
int n;
while(~scanf("%d",&n),n)
{
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if(a[i]>a[j])
dp[i]=max(dp[i],dp[j]+a[i]);
}
dp[i]=max(dp[i],a[i]);
ans=max(ans,dp[i]);
}
cout<<ans<<endl;
}
}
Piggy-Bank
思路:
简单完全背包问题(注意输出后面有个点)
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
using namespace std;
const int maxn = 1e5+10;
struct coin{
int p,w;
}a[maxn];
int dp[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
int ans=0;
memset(dp,0x3f,sizeof dp);
dp[0]=0;
int w1,w2;
cin>>w1>>w2;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].p>>a[i].w;
}
int w=w2-w1;
for(int i=1;i<=n;i++)
{
for(int j=a[i].w;j<=w;j++)
{
dp[j]=min(dp[j],dp[j-a[i].w]+a[i].p);
}
}
if(dp[w]==0x3f3f3f3f) cout<<"This is impossible."<<endl;
else cout<<"The minimum amount of money in the piggy-bank is "<<dp[w]<<"."<<endl;
}
}
免费馅饼
思路:
定义$dp[i][j]$表示第i秒第j个位置可以接到最大数量的馅饼。转移态方程很容易想到$dp[i][j]=max(dp[i][j],max({dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1]})+mp[j][i])$因为要么就是当前位置转移,要么就是距离为1的位置转移。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
int mp[15][maxn];
int dp[maxn][15];
int main(void)
{
int N;
while(cin>>N)
{
memset(mp,0,sizeof(mp));
if(!N)break;
int T=0;
for(int i=0;i<N;i++)
{
int x,y;
scanf("%d %d",&x,&y);
mp[x+1][y]++;
T=max(T,y);
}
memset(dp,-INF,sizeof(dp));
dp[0][6]=0;
for(int i=1;i<=T;i++)
{
for(int j=1;j<=11;j++)
dp[i][j]=max(dp[i][j],max({dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1]})+mp[j][i]);
}
int ans=0;
for(int i=1;i<=11;i++)ans=max(ans,dp[T][i]);
cout<<ans<<endl;
}
return 0;
}
Tickets
思路:
定义$dp[i]$表示前i个人所需的最小时间,转移态方程为$dp[i]=min(dp[i-1]+a[i],dp[i-2]+b[i])$因为当前转态要么就是第i-1个人加上第i个人的票价a[i],要么就是第i-2个人加上第i-1和第i个人共同的票价b[i]
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
const int maxn = 2010;
int dp[maxn];
int a[maxn];
int b[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof dp);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=2;i<=n;i++)
{
scanf("%d",&b[i]);
}
dp[1]=a[1];
dp[2]=min(a[2]+a[1],b[2]);
for(int i=3;i<=n;i++)
{
dp[i]=min(dp[i-1]+a[i],dp[i-2]+b[i]);
}
int h=dp[n]/3600+8;
int m=dp[n]%3600/60;
int s=dp[n]%60;
if(h>12)
printf("%02d:%02d:%02d pm\n", h-12, m, s);
else
printf("%02d:%02d:%02d am\n", h, m, s);
}
return 0;
}
最少拦截系统
思路:
lis模板题了$dp[j]=max(dp[i]+1,dp[j])$
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
const int maxn = 30010;
int dp[maxn];
int a[maxn];
int main()
{
int n;
while(~scanf("%d",&n))
{
// memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i]=1;
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(a[j]>a[i])
dp[j]=max(dp[i]+1,dp[j]);
}
ans=max(dp[i],ans);
}
printf("%d\n",ans);
}
}
FatMouse's Speed
思路:
lis变形,多了一层维度但解题思路类似,转移态方程也相同,题目要求输出方案则记录一下路径。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
const int maxn = 1e5+10;
struct node{
int w,v,id;
}a[maxn];
bool cmp(node a,node b)
{
if(a.w==b.w) return a.v>b.v;
return a.w<b.w;
}
int dp[maxn];
int fa[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n=0;
int w,v;
while(scanf("%d %d",&w,&v)!=EOF)
{
a[++n]={w,v,n};
dp[n]=1;
}
sort(a+1,a+1+n,cmp);
int ans=0;
int id=0;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(a[j].w>a[i].w&&a[j].v<a[i].v)
{
if(dp[j]<dp[i]+1)
{
dp[j]=dp[i]+1;
fa[j]=i;
}
}
}
if(dp[i]>ans)
{
ans=dp[i];
id=i;
}
}
cout<<ans<<endl;
stack <int > st;
while(fa[id])
{
st.push(id);
id=fa[id];
}
st.push(id);
while(st.size())
{
int id=st.top();
st.pop();
cout<<a[id].id<<endl;
}
}
Jury Compromise
1015 -- Jury Compromise (poj.org)
思路:
类似装化为背包问题,没搞得很懂,先放着。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair <int ,int > pii;
const int maxn = 440;
pii a[maxn];
int n,m;
int tot;
int dp[30][maxn*2];
int path[30][maxn*2];
struct edge{
int to,next;
}edge[4*440*210];
void add(int a1,int a2,int b)
{
edge[++tot].to=b;
edge[tot].next=path[a1][a2];
path[a1][a2]=tot;
}
void print(int now)
{
if(!now) return ;
print(edge[now].next);
printf(" %d",edge[now].to);
}
int main()
{
int cnt=0;
while(~scanf("%d%d",&n,&m))
{
cnt++;
if(n==0&&m==0) break;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].first,&a[i].second);
}
memset(dp,-0x3f,sizeof dp);
memset(path,0,sizeof path);
dp[0][maxn]=0;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=1;j--)
{
for(int k=-400;k<=400;k++)
{
if(dp[j-1][k-(a[i].first-a[i].second)+maxn]<0) continue;
if(dp[j-1][k-(a[i].first-a[i].second)+maxn]+a[i].first+a[i].second>dp[j][k+maxn])
{
dp[j][k+maxn]=dp[j-1][k-(a[i].first-a[i].second)+maxn]+a[i].first+a[i].second;
path[j][k+maxn]=path[j-1][k-(a[i].first-a[i].second)+maxn];
add(j,k+maxn,i);
}
}
}
}
int ans=0,id=0,pp=0,dd=0;
for(int i=0;i<=400;i++)
{
ans=dp[m][i+maxn];id=i;
if(dp[m][maxn-i]>ans) ans=dp[m][maxn-i],id=-i;
if(ans>=0) break;
}
pp=(ans+id)/2;dd=(ans-id)/2;
printf("Jury #%d\n",cnt);
printf("Best jury has value %d for prosecution and value %d for defence:\n",pp,dd);
print(path[m][id+maxn]);
printf("\n\n");
}
}
Common Subsequence
1458 -- Common Subsequence (poj.org)
思路:
这题也是偏模板,用两个指针分别指向两个字符串,当指向的字符相同时,$dp[i][j]=dp[i-1][j-1]+1$,否则$dp[i][j]=max(dp[i][j-1],dp[i-1][j])$
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
const int maxn = 510;
int dp[maxn][maxn];
int main()
{
string s1,s2;
while(cin>>s1>>s2)
{
memset(dp,0,sizeof dp);
for(int i=1;i<=s1.size();i++)
{
for(int j=1;j<=s2.size();j++)
{
if(s1[i-1]==s2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
}
}
cout<<dp[s1.size()][s2.size()]<<endl;
}
}
Help Jimmy
思路:
题目意思好懂,可能代码写起来麻烦一点,就是开个二维$dp[20000][2]$第二维表示第i块砖左边或右边到地面的距离。转移态方程以左边为例$dp[i][0]=a[i].h-a[k].h+min(dp[k][0]+a[i].x1-a[k].x1,dp[k][1]+a[k].x2-a[i].x1)$注意状态转移的条件就行。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 2e4+10;
const int inf = 0x3f3f3f;
struct node{
int x1,x2,h;
}a[maxn];
int n,x,y,m;
bool cmp(node a,node b)
{
return a.h>b.h;
}
int dp[maxn][2];
void left(int i)
{
int k=i+1;
while(k<n+1&&a[i].h-a[k].h<=m)
{
if(a[i].x1>=a[k].x1&&a[i].x1<=a[k].x2)
{
dp[i][0]=a[i].h-a[k].h+min(dp[k][0]+a[i].x1-a[k].x1,dp[k][1]+a[k].x2-a[i].x1);
return ;
}
k++;
}
if(a[i].h-a[k].h>m)
dp[i][0]=inf;
else
dp[i][0]=a[i].h;
return ;
}
void right(int i)
{
int k=i+1;
while(k<n+1&&a[i].h-a[k].h<=m)
{
if(a[i].x2>=a[k].x1&&a[i].x2<=a[k].x2)
{
dp[i][1]=a[i].h-a[k].h+min(dp[k][0]+a[i].x2-a[k].x1,dp[k][1]+a[k].x2-a[i].x2);
return;
}
k++;
}
if(a[i].h-a[k].h>m)
dp[i][1]=inf;
else
dp[i][1]=a[i].h;
return ;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
memset(dp,0,sizeof dp);
cin>>n>>x>>y>>m;
a[0].x1=-20000;a[0].x2=20000;a[0].h=0;
a[n+1].x1=a[n+1].x2=x;a[n+1].h=y;
for(int i=1;i<=n;i++)
{
cin>>a[i].x1>>a[i].x2>>a[i].h;
}
sort(a,a+2+n,cmp);
for(int i=n;i>=0;i--)
{
left(i);
right(i);
}
cout<<min(dp[0][0],dp[0][1])<<endl;
}
}
Longest Ordered Subsequence
2533 -- Longest Ordered Subsequence (poj.org)
思路:lis模板题
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
const int maxn = 1100;
int a[maxn];
int dp[maxn];
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i]=1;
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if(a[j]<a[i])
{
dp[i]=max(dp[i],dp[j]+1);
}
}
ans=max(ans,dp[i]);
}
cout<<ans<<endl;
}
}
Treats for the Cows
3186 -- Treats for the Cows (poj.org)
思路:定义$dp[i][j]$表示左端取i个,右端取j个时获得的总价值之和最大。转移态方程为$dp[i][j]=max(dp[i-1][j]+(i+j)a[i],dp[i][j-1]+(i+j)a[n-j+1])$
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
const int maxn = 2100;
typedef long long ll;
int a[maxn];
ll dp[maxn][maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
dp[1][0]=a[1];dp[0][1]=a[n];
ll ans=0;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
if(i+j>n) break;
if(i&&j)
{
dp[i][j]=max(dp[i-1][j]+(ll)(i+j)*a[i],dp[i][j-1]+(ll)(i+j)*a[n-j+1]);
continue;
}
if(i) dp[i][j]=dp[i-1][j]+(ll)(i+j)*a[i];
if(j) dp[i][j]=dp[i][j-1]+(ll)(i+j)*a[n-j+1];
}
}
for(int i=0;i<=n;i++)
{
ans=max(ans,dp[i][n-i]);
}
cout<<ans<<endl;
}
FatMouse and Cheese
思路:读完题目便想到了记忆化搜素,每次搜索四个方向以及移动的距离,用数组$dp[i][j]$记录一下当前状态。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
const int maxn = 110;
typedef long long ll;
int n,m;
ll dp[maxn][maxn];
int a[maxn][maxn];
int vis[maxn][maxn];
int ans=0;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
ll dfs(int x,int y)
{
if(dp[x][y]) return dp[x][y];
ll mx=0;
for(int i=1;i<=m;i++)
{
for(int j=0;j<4;j++)
{
int x1=x+i*dir[j][0];
int y1=y+i*dir[j][1];
if(x1<=0||y1<=0||x1>n||y1>n) continue;
if(a[x1][y1]<=a[x][y]) continue;
mx=max(mx,dfs(x1,y1)+a[x1][y1]);
// cout<<"mx="<<mx<<endl;
}
}
return dp[x][y]+=mx;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n+m==-2) break;
ans=0;
memset(vis,0,sizeof vis);
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
}
}
dfs(1,1);
dp[1][1]+=a[1][1];
printf("%d\n",dp[1][1]);
}
}
Phalanx
思路:这道题是用dp找最大的对称子图。定义$dp[i][j]$表示以坐标$(i,j)$为左下角时最大的对称子图。$dp[i][j]$的状态可以由$dp[i-1][j+1]$的状态转移过来,只要满足$s[i-k][j]==s[i][k+j]$便可不断更新$dp[i][j]$
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
const int maxn = 1010;
char s[maxn][maxn];
int dp[maxn][maxn];
int main()
{
int n;
while(~scanf("%d",&n),n)
{
memset(dp,0,sizeof dp);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cin>>s[i][j];
}
}
int ans=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
dp[i][j]=1;
if(i==0||j==n-1) continue;
int m = dp[i-1][j+1];
for(int k=1;k<=m;k++)
{
if(s[i-k][j]==s[i][k+j])
dp[i][j]++;
else
break;
}
ans=max(ans,dp[i][j]);
}
}
cout<<ans<<endl;
}
}
Milking Time
3616 -- Milking Time (poj.org)
思路:
先对开始时间进行一个升序排序,然后类似一个简单lis
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
const int maxn = 1e6+10;
typedef long long ll;
struct node{
int x,y,w;
}a[maxn];
bool cmp(node a,node b)
{
return a.x<b.x;
}
ll dp[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n,m,r;
cin>>n>>m>>r;
for(int i=1;i<=m;i++)
{
cin>>a[i].x>>a[i].y>>a[i].w;
a[i].y+=r;
// dp[i]=a[i].w;
}
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++) dp[i]=a[i].w;
ll ans=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=i;j++)
{
if(a[i].x>=a[j].y)
dp[i]=max(dp[i],dp[j]+a[i].w);
}
ans=max(ans,dp[i]);
}
for(int i=1;i<=m;i++) ans=max(ans,dp[i]);
cout<<ans<<endl;
}
Making the Grade
3666 -- Making the Grade (poj.org)
思路:分别考虑构建成不递增的序列和不递减的序列,这里以不递增的序列为例。定义$dp[i][j]$表示前i个数中且第i个数为第j大时所需的最低费用。转移态方程$dp[i][j]=min(dp[i-1][k])+abs(a[i]-b[j])$
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
const int maxn = 2e3+10;
typedef long long ll;
int a[maxn];
int b[maxn];
ll dp1[maxn][maxn];
ll dp2[maxn][maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n;
ll ans=1ll<<62;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
{
ll x=1ll<<62;
for(int j=1;j<=n;j++)
{
x=min(x,dp1[i-1][j]);
dp1[i][j]=x+abs(a[i]-b[j]);
}
}
for(int i=1;i<=n;i++)
{
ans=min(ans,dp1[n][i]);
}
reverse(b+1,b+1+n);
for(int i=1;i<=n;i++)
{
ll x=1ll<<62;
for(int j=1;j<=n;j++)
{
x=min(x,dp2[i-1][j]);
dp2[i][j]=x+abs(a[i]-b[j]);
}
}
for(int i=1;i<=n;i++)
{
ans=min(ans,dp2[n][i]);
}
cout<<ans<<endl;
}