A - Max Sum Plus Plus
HDU - 1024 最大M子段和问题
首先,定义数组a[n],dp[m][n].
a[n]用来存储n个整数组成的序列.
dp[i][j]用来表示由前 j项得到的含i个字段的最大值,且最后一个字段以a[j]项结尾。仔细想想,我们可以知道:
dp[i][j]=max(dp[i][j-1]+a[j],dp(i-1,t)+a[j]) 其中i-1<=t<=j-1.
(因为必须是以 a[j] 结尾的,所以a[j]一定属于最后一个子段,即要么自己独立成一个子段,要么与前边以a[j-1]结尾的子段联合)
所求的最后结果为 max( dp[m][j] ) 其中1<=j<=n.
但是,我们会发现,当n非常大时,这个算法的时间复杂度和空间复杂度是非常高的,时间复杂度近似为O(m*n^2),
空间复杂度近似为O(m*n).因此,我们需要优化算法来降低时间复杂度和空间复杂度.
2.优化算法:
(1)节省时间
由基本思路,我们可以知道,dp[i][j]=max(dp[i][j-1]+a[j],dp(i-1,t)+a[j]),其中i-1<=t<=j-1.我们只要找到dp[i][j-1]
和dp[i-1][t]的最大值加上a[j]即为dp[i][j].所以,定义一个数组Max[n],用Max[j-1]来表示求解dp[i][j]时dp[i-1][t]的最大值
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1000000
#define INF 0x3f3f3f3f
int a[N+10];
int dp[N+10], Max[N+10];
int main()
{
int m, n, maxn;
while(scanf("%d%d", &m, &n)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%d", &a[i]);
}
memset(dp,0,sizeof(dp));
memset(Max,0,sizeof(Max));
for(int i=1;i<=m;i++) //分几段
{
maxn=-INF;
for(int j=i;j<=n;j++) //j个数分成i组,至少要有i个数
{
dp[j]=max(dp[j-1]+a[j], Max[j-1]+a[j]);
Max[j-1]=maxn;
maxn=max(maxn, dp[j]);
}
}
for(int i=1;i<=n;i++)
printf("%d ", Max[i]);
printf("\n%d\n", maxn);
}
return 0;
}
B - Ignatius and the Princess IV
HDU - 1029 就是抵消的思想,看看代码,就懂了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a[1000005];
int main()
{
int n;
while(scanf("%d", &n)!=EOF)
{
int i;
int num=0, ans=0;
for(i=1;i<=n;i++)
{
scanf("%d", &a[i]);
if(num==0)
{
num++;
ans=a[i];
}
else
{
if(a[i]==ans)
{
num++;
}
else
{
num--;
}
}
}
printf("%d\n", ans);
}
return 0;
}
C - Monkey and Banana
HDU - 1069 (2个)最长上升子序列, 不解释
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int i, j, k, n;
struct node
{
int x, y, z;
}s[10000005];
bool cmp(node a, node b)
{
if(a.x==b.x)
{
return a.y<b.y;
}
return a.x<b.x;
};
int dp[1005];
int maxn;
int DP()
{
for(i=0;i<=j;i++)
{
dp[i]=s[i].z;
for(k=i-1;k>=0;k--)
{
if(s[i].x>s[k].x&&s[i].y>s[k].y)
dp[i]=max(dp[i], dp[k]+s[i].z);
}
if(dp[i]>maxn)
maxn=dp[i];
}
return maxn;
}
int main()
{
int num=0;
while(scanf("%d", &n)!=EOF&&n!=0)
{
int a, b, c;
j=0;
maxn=0;
for(i=0;i<n;i++)
{
scanf("%d%d%d", &a, &b, &c);
s[j].x=max(a, b);
s[j].y=min(a, b);
s[j].z=c;
s[j+1].x=max(c, b);
s[j+1].y=min(c, b);
s[j+1].z=a;
s[j+2].x=max(a, c);
s[j+2].y=min(a, c);
s[j+2].z=b;
j=j+3;
}
sort(s, s+j, cmp);
printf("Case %d: maximum height = %d\n", ++num, DP() );
}
return 0;
}
Doing Homework
HDU - 1074 状压dp,耐心看,就能看懂,没时间就不看了
参加运算的两个数据,按二进制位进行“与”运算。
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:两位同时为“1”,结果才为“1”,否则为0
例如:3&5 即 0000 0011& 0000 0101 = 00000001 因此,3&5的值得1。
按位或运算符(|)
参加运算的两个对象,按二进制位进行“或”运算。
运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1;
即 :参加运算的两个对象只要有一个为1,其值为1。
例如:3|5 即 00000011 | 0000 0101 = 00000111 因此,3|5的值得7。
异或运算符(^)
参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
“异或运算”的特殊作用:
使特定位翻转找一个数,对应X要翻转的各位,该数的对应位为1,其余位为零,此数与X对应位异或即可。
例:X=10101110,使X低4位翻转,用X ^0000 1111 = 1010 0001即可得到。
左移运算符(<<)
将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
例:a = a<< 2将a的二进制位左移2位,右补0,
左移1位后a = a *2;
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。
右移运算符(>>)
将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
操作数每右移一位,相当于该数除以2。
例如:a = a>> 2 将a的二进制位右移2位,
左补0 or 补1得看被移数是正还是负。
#include<stdio.h>
#include<string.h>
const int MAXN=1<<16;
//1<<16
//左移16位
//0000000000000001(2)==1
//1000000000000000(2)==2^16==65536
struct Node
{
int cost;//所需时间
int pre;//前一状态
int reduced;//最少损失的分数
} dp[MAXN];
bool visited[MAXN];
struct Course
{
int deadtime;//截止日期
int cost;//所需日期
char name[201];
} course[16];
void output(int status)
{
int curjob=dp[status].pre^status;
int curid=0;
curjob>>=1;
while(curjob)
{
curid++;
curjob>>=1;
}
if(dp[status].pre!=0)
{
output(dp[status].pre);
}
printf("%s\n",course[curid].name);
}
int main()
{
int T,n,i,j;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int upper=1<<n; //若n==3,upper=1000(2)
printf("%d\n", upper);
int dayupper=0;
for(i=0; i<n; i++)
{
scanf("%s%d%d",course[i].name,&course[i].deadtime,&course[i].cost);
dayupper+=course[i].cost;
}
memset(visited,false,sizeof(visited));
dp[0].cost=0;
dp[0].pre=-1;
dp[0].reduced=0;//dp[0]是指所有作业都没有做的状态
visited[0]=true;
int work;
int tupper=upper-1;//tupper表示成二进制数是n个1的,表示所有的作业都完成了,tupper=111(2)
for(j=0; j<tupper; j++) //遍历所有状态
{
for(work=0; work<n; work++)
{
int cur=1<<work; //0000000,0000010, 0000100
if((cur&j)==0)//该项作业尚未做过
{
//printf("***%d %d\n",j, cur);
int curtemp=cur|j; //我也不懂,大佬这么解释,在“j”状态的基础上,增加作业cur
int day=dp[j].cost+course[work].cost;
dp[curtemp].cost=day;
int reduce=day-course[work].deadtime;
if(reduce<0)reduce=0;
reduce+=dp[j].reduced;
if(visited[curtemp])//该状态已有访问信息
{
if(reduce<dp[curtemp].reduced)
{
dp[curtemp].reduced=reduce;
dp[curtemp].pre=j;
}
//else if(reduce==dp[curtemp].reduced)
//扣分相同,取字典序小的那一个,由于这里j是按从小到达搜索的,默认已是按字典序,不需再处理
// {
// if(dp[curtemp].pre>j)
// dp[curtemp].pre = j;
// }
}
else //没有访问过
{
visited[curtemp]=true;
dp[curtemp].reduced=reduce;
dp[curtemp].pre=j;
}
}
}
}
printf("%d\n",dp[tupper].reduced);
output(tupper);//递归输出
}
return 0;
}
E - Super Jumping! Jumping! Jumping!
HDU - 1087 最长上升子序列
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a[1005], sum[1005];
int main()
{
int n;
while(scanf("%d", &n)!=EOF&&n!=0)
{
int i, j;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=a[i];
}
int maxn=0;
for(i=1;i<=n;i++)
{
for(j=i-1;j>=1;j--)
{
if(a[i]>a[j])
{
sum[i]=max(sum[i], sum[j]+a[i]);
}
if(maxn<sum[i])
{
maxn=sum[i];
}
}
}
printf("%d\n", maxn);
}
}
Piggy-Bank
HDU - 1114 完全背包问题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
int a[10005], b[10005], dp[10005];
int main()
{
int t;
scanf("%d", &t);
int e, f, n;
while(t--)
{
scanf("%d%d", &e, &f);
scanf("%d", &n);
int i, j;
for(i=0;i<n;i++)
{
scanf("%d%d", &a[i], &b[i]);
}
for(i=0;i<=f-e;i++)
{
dp[i]=INF;
}
dp[0]=0;
for(i=0;i<n;i++)
{
for(j=b[i];j<=f-e;j++)
{
dp[j]=min(dp[j], dp[j-b[i]]+a[i]);
}
}
if(dp[f-e]==INF)
{
printf("This is impossible.\n");
}
else
{
printf("The minimum amount of money in the piggy-bank is %d.\n", dp[f-e]);
}
}
return 0;
}
01背包
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
f[i][v]=max{ f[i-1][v], f[i-1][v-w[i]]+v[i] }。
可以压缩空间,f[v]=max{f[v],f[v-w[i]]+v[i]}
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-w[i]的背包中”,此时能获得的最大价值就是f [i-1][v-w[i]]再加上通过放入第i件物品获得的价值v[i]。
void solve()
{
for (int i = 0; i < n; i++)
{
for (int j = W; j >= w[i]; j--) //从后往前循环
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
cout << dp[W];
}
完全背包
这个问题非常类似于01背包问题,所不同的是每种物品有无限件。也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取0件、取1件、取2件……等很多种。若两件物品i、j满足c<=c[j]且w>=w[j],则将物品j去掉,不用考虑。这个优化的正确性显然:任何情况下都可将价值小体积高的j换成物美价廉的i,得到至少不会更差的方案
void solve()
{
for (int i = 0; i < n; i++)
{
for (int j = w[i]; j <= W; j++) //从前往后循环
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
cout << dp[W];
}
需要注意的是两个问题的j的循环方向不同,前者需要从后往前循环,因为数组后面需要用到前面改变之前的值,而后者是针对的对象是自己。
G - 免费馅饼
HDU - 1176 数塔问题,注意前期处理
dp[j][i]=max(dp[j][i+1],max(dp[j+1][i+1],dp[j-1][i+1]))+dp[j][i];
#include<stdio.h>
#include<string.h>
int dp[14][100010];
//时间,位置
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
int main()
{
int n,t,i,j,x,tt;
while(scanf("%d",&n)&&n)
{
tt=0;
memset(dp,0,sizeof(dp));
while(n--)
{
scanf("%d%d",&x,&t);
dp[x+1][t]++; //x+1
tt=max(t,tt);
}
for(i=tt-1; i>=0; i--)
{
for(j=1; j<=11; j++)
{
dp[j][i]=max(dp[j][i+1],max(dp[j+1][i+1],dp[j-1][i+1]))+dp[j][i];
}
}
printf("%d\n",dp[6][0]);
}
return 0;
}
Tickets
看懂输入,就懂啦。。。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int k, a[2005], b[2005], time[2005];
memset(time, 0, sizeof(time));
scanf("%d", &k);
for(int i=1;i<=k;i++)
{
scanf("%d", &a[i]);
}
for(int i=2;i<=k;i++)
{
scanf("%d", &b[i]);
}
time[1]=a[1];
for(int i=2;i<=k;i++)
{
time[i]=min(time[i-1]+a[i], time[i-2]+b[i]);
}
int s=time[k]%60;
int f=(time[k]/60)%60;
int h=time[k]/3600+8;
printf("%.2d:%.2d:%.2d ", h, f, s);
if(h>=13)
printf("pm\n");
else
printf("am\n");
}
}
最少拦截系统
HDU - 1257 自定义:最少下降子序列个数
#include <cstdio>
#include <algorithm>
using namespace std;
int a[30005], b[30005];
int main()
{
int n;
while(scanf("%d", &n)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf("%d", &a[i]);
b[i]=1;
}
int maxn=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<i;j++)
{
if(a[i]>a[j]&&b[i]<=b[j])
{
b[i]+=1;
}
}
maxn=max(maxn, b[i]);
}
printf("%d\n", maxn);
}
return 0;
}
FatMouse's Speed
HDU - 1160 (2个)最长上升子序列+输出
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
struct node
{
int w;
int s;
int no;
} mice[1111];
struct node1
{
int pre;
int num;
} dp[1111];
bool cmp(node a,node b)
{
if(a.w==b.w)
return a.s>b.s;
return a.w<b.w;
}
int main()
{
int i,j,k,max,t;
i=0;
while(scanf("%d%d",&mice[i].w,&mice[i].s)!=EOF)
{
mice[i].no=i+1;
i++;
//if(i==9)
// break;
}
sort(mice,mice+i,cmp);
//printf("\n");
//for(j=0;j<i;j++)
// printf("%d %d\n",mice[j].w,mice[j].s);
//printf("\n");
for(j=0; j<i; j++)
{
dp[j].num=1;
dp[j].pre=0;
}
max=1;
t=1;
for(j=1; j<i; j++)
{
for(k=0; k<j; k++)
{
if(mice[k].s>mice[j].s&&mice[k].w<mice[j].w)
{
if(dp[j].num<dp[k].num+1)
{
dp[j].num=dp[k].num+1;
dp[j].pre=k; //优秀
}
}
}
if(dp[j].num>max)
{
max=dp[j].num;
t=j;
}
}
printf("%d\n",max);
int m[1111];
for(j=1; j<=max; j++)
{
m[j]=t;
t=dp[t].pre;
}
for(k=max; k>=1; k--)
printf("%d\n",mice[m[k]].no);
return 0;
}
Jury Compromise
POJ - 1015 没做,百度题解看吧
https://blog.csdn.net/glqac/article/details/22687243
Help Jimmy
当Jimmy落在一个平台上后有两种选择(向左走或向右走),而Jimmy走到平台左边和右边的时间很容易计算,如果我们得到了以平台左边为起点及以平台右边为起点到地面的最短时间,那么选择往左走还是往右走就很容易了。这样,原问题就分解为两个子问题这两个子问题和原问题的形式是一致的了,也就找到了“状态”dp[i][j], j = 0, 1 (dp[i][0]表示以i号平台左边为起点到地面的最短时间,dp[i][1]]表示以i号平台右边为起点到地面的最短时间)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int INF=0x3f3f3f3f;
struct node
{
int x1, x2, h;
bool operator<(node b)const
{
return h>b.h;//从大到小排序,据说速度快
}
}a[1005];
//bool cmp(node a, node b)
//{
// return a.h>b.h;
//}
int dp[1005][2];
int n, max_h;
void left(int i)//这个左右只是这一步的左右
{
int k=i+1;
while(k<n+1&&a[i].h-a[k].h<=max_h)
{
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>max_h)
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<=max_h)
{
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>max_h)
dp[i][1]=INF;
else
dp[i][1]=a[i].h;
return ;
}
int main()
{
int t, x, y;
scanf("%d", &t);
while(t--)
{
scanf("%d%d%d%d", &n, &x, &y, &max_h);
a[0].x1=-20000;
a[0].x2=20000;
a[0].h=0;
a[1].x1=a[1].x2=x;
a[1].h=y;
for(int i=2;i<=n+1;i++)
{
scanf("%d%d%d", &a[i].x1, &a[i].x2, &a[i].h);
}
sort(a, a+n+2);
memset(dp, 0, sizeof(dp));
for(int i=n;i>=0;i--)
{ // printf("%d\n", i);
left(i);
right(i);
}
int time=min(dp[0][0], dp[0][1]);
printf("%d\n", time);
}
return 0;
}
Longest Ordered Subsequence
#include <stdio.h>
#include <string.h>
int main()
{
int n, i, k, a[1005], maxlen[1005];
scanf("%d", &n);
maxlen[1]=1;
for(i=1;i<=n;i++)
{
scanf("%d", &a[i]);
}
for(k=2;k<=n;k++)
{
int m=0;
for(i=1;i<k;i++)
{
if(a[i]<a[k]&&m<maxlen[i])
{
m=maxlen[i];
}
}
maxlen[k]=m+1;
}
for(i=1;i<=n;i++)
{
if(maxlen[i]>maxlen[1])
{
maxlen[1]=maxlen[i];
}
}
printf("%d\n", maxlen[1]);
return 0;
}
Treats for the Cows
POJ - 3186 区间dp
先暴力打一发,试试
由里向外逆推区间
dp[i][j]=max(dp[i+1][j]+a[i]*(n+i-j), dp[i][j-1]+a[j]*(n+i-j));
(n+i-j)是时间,你推一推,应该就懂了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[2002][2002];
int a[2002];
int main()
{
int n;
scanf("%d", &n);
for(int i=1;i<=n;i++)
{
scanf("%d", &a[i]);
}
for(int i=n;i>0;i--)
{
for(int j=i;j<=n;j++)
dp[i][j]=max(dp[i+1][j]+a[i]*(n+i-j), dp[i][j-1]+a[j]*(n+i-j));
//& & & &(n-3)
// & & &(n-2)
// & &(n-1)
// &(n)
//看懂了没,看不懂就算了
}
printf("%d\n", dp[1][n]);
return 0;
}
FatMouse and Cheese
记忆搜索,可以去看看 滑雪。。。现在看看当时自己滑雪的优化,都感觉自己好强
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=105;
int vis[N][N], Map[N][N];
int n, k;
int dir[4][2]={{1,0},{0,1},{0,-1},{-1,0}};
int dfs(int x, int y)
{
if(vis[x][y]!=0)
return vis[x][y];
int maxn=0;
for(int j=0;j<4;j++)
for(int i=1;i<=k;i++)
{
int xx=x+dir[j][0]*i;
int yy=y+dir[j][1]*i;
if(xx>=0&&xx<n&&yy>=0&&yy<n&&Map[x][y]<Map[xx][yy])
maxn=max(maxn, dfs(xx, yy));
}
vis[x][y]=maxn+Map[x][y];
return vis[x][y];
}
int main()
{
while(scanf("%d%d", &n, &k)!=EOF&&n!=-1&&k!=-1)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
scanf("%d", &Map[i][j]);
}
}
memset(vis, 0, sizeof(vis));
printf("%d\n", dfs(0, 0));
}
return 0;
}
Q - Phalanx
对称??? 不太懂
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[1006][1005];
char a[1005][1005];
int main()
{
int n, ans;
while(scanf("%d", &n)!=EOF&&n)
{
ans=1;
for(int i=0;i<n;i++)
{
scanf("%s", a[i]);
dp[0][i]=1;
}
for(int i=1;i<n;i++)
{
for(int j=n-1;j>=0;j--)
{
// dp[i][j]=1;
// if(i==0||j==n-1)
// continue;
// int k=dp[i-1][j+1];
// for(int s=1;s<=k;s++)
// {
// if(a[i-s][j]==a[i][j+s])
// dp[i][j]++;
// else
// break;
// }
int x=i-1, y=j+1;
int num=1;
while(x>=0&&x<n&&y>=0&&y<n&&a[x][j]==a[i][y])
{
num++;
x--;
y++;
}
if(num>dp[i-1][j+1])
dp[i][j]=dp[i-1][j+1]+1;
else
dp[i][j]=num;
ans=max(ans, dp[i][j]);
}
}
printf("%d\n", ans);
}
return 0;
}
R - Milking Time
最大递增子序列:挤奶工每次挤奶必须挤完完整的时间段,且每次挤完需要休息r时,
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1005;
int dp[N];
struct node
{
int l, r, v;
bool operator<(const node &t)const
{
return l<t.l;
}
}a[N];
int main()
{
int n, m, r;
scanf("%d%d%d", &n, &m, &r);
for(int i=0;i<m;i++)
{
scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].v);
}
sort(a, a+m);
int ans=0;
for(int i=0;i<m;i++)
{
int maxn=0;
for(int j=0;j<i;j++)
{
if(a[j].r+r<=a[i].l&&maxn<dp[j])
maxn=dp[j];
}
dp[i]=maxn+a[i].v;
ans=max(ans, dp[i]);
}
printf("%d\n", ans);
return 0;
}
Making the Grade
https://www.cnblogs.com/vb4896/p/5877962.html
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2010;
const int inf=0x3f3f3f3f;
int dp[maxn][maxn];
int a[maxn], b[maxn];
int n;
int main()
{
scanf("%d", &n);
for(int i=1;i<=n;i++)
{
scanf("%d", &a[i]);
b[i]=a[i];
}
sort(b+1, b+n+1); //排序的目的是为了dp[1][j]
for(int i=1;i<=n;i++) //第i个数,dp[i][1]至少等于i-1个数
{
int m=inf;
for(int j=1;j<=n;j++)
{
m=min(m, dp[i-1][j]);
dp[i][j]=abs(a[i]-b[j])+m;
}
}
// for(int i=1;i<=n;i++) //打开试试!!!
// {
// for(int j=1;j<=n;j++)
// printf("%d ", dp[i][j]);
// printf("\n");
// }
int ans=inf;
for(int i=1;i<=n;i++)
{
ans=min(ans, dp[n][i]);
}
printf("%d\n", ans);
return 0;
}