acm之旅--保研练习系列

文章目录

          • A - Max Sum Plus Plus
          • B - Ignatius and the Princess IV
          • C - Monkey and Banana
          • D - Doing Homework
          • E - Super Jumping! Jumping! Jumping!
          • F - Piggy-Bank
          • G - 免费馅饼
          • H - Tickets
          • I - 最少拦截系统
          • J - FatMouse's Speed
          • K - Jury Compromise
          • L - Common Subsequence
          • M - Help Jimmy
          • N - Longest Ordered Subsequence
          • O - Treats for the Cows
          • P - FatMouse and Cheese
          • Q - Phalanx
          • R - Milking Time
          • S - Making the Grade

题目链接: 保研练习系列

A - Max Sum Plus Plus

题目链接:A - Max Sum Plus Plus
参考博文:Max Sum Plus Plus (动态规划+m子段和的最大值)

  • 思路:
    • 状态构造:dp[i][j]表示前j个数分成i组(第j个数字一定在组内)的最大和。
    • max(dp[m][j])
    • 动态转移方程:dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k])+a[j]) (0 dp[i][j-1]+a[j]表示的是前j-1分成i组,第j个必须放在前一组里面。
      max( dp[i-1][k] ) + a[j] )表示的前(0

代码:

#include 
#include 
#include 
using namespace std;

const int MAX = 1000005;
int dp[MAX], a[MAX];
int Max[MAX];

int main()
{
    int n, m;
    while(cin >> m >> n)
    {
        for(int i=1; i<=n; i++)
        cin >> a[i];
        memset(dp, 0, sizeof(dp));
        memset(Max, 0, sizeof(Max));
        int mmax;//表示当前最大值
        for(int i=1; i<=m; i++)
        {
            mmax = -1<<29;
            for(int j=i; j<=n; j++)
            {
                dp[j] = max(dp[j-1]+a[j], Max[j-1]+a[j]);
                Max[j-1] = mmax;//更新后用于下一个i循环,代表max(dp[i-1][k])
                mmax = max(mmax, dp[j]);//一直保持在前
            }
        }
        cout << mmax << endl;
    }
    return 0;
}
B - Ignatius and the Princess IV

题目链接:B - Ignatius and the Princess IV

  • 思路:水题。

代码:

#include 
#include 
#include 
#include 
using namespace std;

typedef long long LL;
map<LL, LL> m;
int main()
{
    int N;
    LL a, ans;
    while(scanf("%d", &N)!=EOF)
    {
        m.clear();
        int res = (N+1)/2;
        for(int i=0; i<N; i++)
        {
            scanf("%lld", &a);
            m[a]++;
            if(m[a]==res) ans = a;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
C - Monkey and Banana

题目链接:C - Monkey and Banana

  • 思路:这是《算法竞赛入门(第二版)》中的一道题,具体见中的437题。难点在与状态的构造,使用维这一个变量,简化的方法。

代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;

int a[50][5], dp[50][5], n;

void Init(int n)
{
    for(int i=0; i<=n; i++)
    {
        for(int j=0; j<3; j++)
            dp[i][j] = 0;
    }
}
bool check(int i, int j, int k, int t)
{
    vector<int> m1, m2;
    for(int h=0; h<3; h++)
    {
        if(h!=j) m1.push_back(a[i][h]);
        if(h!=t) m2.push_back(a[k][h]);
    }
    if(m1[0]<=m2[0] || m1[1]<=m2[1]) return 0;
    else                             return 1;
}
int DP(int i, int j)
{
    int &ans = dp[i][j];
    if(ans>0) return ans;
    ans = a[i][j];
    for(int k=1; k<=n; k++)
    {
        for(int t = 0; t<3; t++)
        {
            if(check(i, j, k, t))
            {
                ans = max(ans, DP(k, t)+a[i][j]);
            }
        }
    }
    return ans;
}
int main()
{
    int kase = 1;
    while(scanf("%d", &n)!=EOF && n)
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d%d", &a[i][0], &a[i][1], &a[i][2]);
            sort(a[i], a[i]+3);
        }
        Init(n);
        int Max = 0;
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<3; j++)
            {
                Max = max(Max, DP(i, j));
            }
        }
        printf("Case %d: maximum height = %d\n", kase++, Max);
    }
    return 0;
}

D - Doing Homework

题目链接:D - Doing Homework

E - Super Jumping! Jumping! Jumping!

题目链接:E - Super Jumping! Jumping! Jumping!

  • 思路:LIS的简单变体,由求最长递增子序列的长度变为求最长递增子序列的和,套用即可。

代码:

#include 
#include 
#include 
using namespace std;

int main()
{
    int N, a[1005], dp[10005];
    while(cin >> N && N)
    {
        for(int i=1; i<=N; i++)
            cin >> a[i];
        memset(dp, 0, sizeof(dp));
        a[0] = -1<<29;
        int ans = 0;
        for(int i=1; i<=N; i++)
        {
            for(int j=0; j<i; j++)
            {
                if(a[j]<a[i])
                    dp[i] = max(dp[i], dp[j]+a[i]);
            }
            ans = max(ans, dp[i]);
        }
        cout << ans << endl;
    }
    return 0;
}
F - Piggy-Bank

题目链接:F - Piggy-Bank

  • 思路:一个完全背包的模板题,注意数组不要开小了。。。。

代码:

#include 
#include 
#include 
using namespace std;

const int INF = 1<<29;
struct Node
{
    int p, w;
};
Node coin[1000];
int dp[10005];//注意数组大小,开小了可能TLE

int main()
{
    int T, E, F, N, n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &E, &F);
        N = F-E;
        scanf("%d", &n);
        fill(dp, dp+N+1, INF);
        dp[0] = 0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d", &coin[i].p, &coin[i].w);
            for(int j=coin[i].w; j<=N; j++)
            {
                dp[j] = min(dp[j], dp[j-coin[i].w]+coin[i].p);
            }
        }
        if(dp[N]==INF)
            printf("This is impossible.\n");
        else
            printf("The minimum amount of money in the piggy-bank is %d.\n", dp[N]);
     }
    return 0;
}
G - 免费馅饼

题目链接:G - 免费馅饼
参考博文: G. 免费馅饼

  • 思路:动态规划题目。

代码:

#include 
#include 
#include 
using namespace std;

int a[100005][11], dp[100005][11];//a[i][j]为第i秒的j位置掉下的馅饼数量,dp[i][j]表示第i秒在j位置总共接了多少个馅饼
int dx[5] = {-1, 0, 1};
int DP(int i, int j)
{
    int &ans = dp[i][j];
    if(ans>0 || i==1) return ans;
    int st = 0, ed = 3;
    if(j==0) st = 1;
    else if(j==10) ed = 2;
    for(int k=st; k<ed; k++)
    {
        ans = max(ans, DP(i-1, j+dx[k])+a[i][j]);
    }
    return ans;
}
int main()
{
    int n, b, c;
    while(scanf("%d", &n)!=EOF && n)
    {
        int T = 0;
        memset(a, 0, sizeof(a));
        memset(dp, 0, sizeof(dp));
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d", &b, &c);
            a[c][b]++;
            if(T<c) T = c;
        }
        dp[1][4] = a[1][4], dp[1][5] = a[1][5], dp[1][6] = a[1][6];
        int Max = 0;
        for(int j=0; j<=10; j++)
        {
            Max = max(Max, DP(T, j));
        }
        printf("%d\n", Max);
    }
    return 0;
}
H - Tickets

题目链接:H - Tickets

  • 思路:
    • 状态构造:dp[i]表示前i个人所需取票的时间。
    • 终态:dp[K],K为总人数。
    • 状态转移方程:dp[i] = min(dp[i-1]+a[i], dp[i-2]+b[i-1]),a[i]表示第i个人取票的时间,b[i-1]表示第i-1和第i个人一起取票的时间。
    • 初始化:dp[0] = 0.
      我使用了记忆化搜索。

代码:

#include 
#include 
#include 
#include 
using namespace std;

const int MAX = 5000;
int a[MAX], b[MAX], dp[MAX];
int DP(int i)
{
    if(i<0) return 0;//注意i<0的情况
    int &ans = dp[i];
    if(ans!=-1) return ans;
    if(i>1) ans = min(DP(i-1)+a[i], DP(i-2)+b[i-1]);
    else    ans = a[i];//i==1时,为a[1]
    return ans;
}
int main()
{
    int N, K;
    scanf("%d", &N);
    while(N--)
    {
        vector<int> ans;
        scanf("%d", &K);
        for(int i=1; i<=K; i++)
        {
            scanf("%d", &a[i]);
        }
        for(int i=1; i<K; i++)
        {
            scanf("%d", &b[i]);
        }
        memset(dp, -1, sizeof(dp));
        dp[0] = 0;
        int t = DP(K);
        //换算时间
        int h = t/3600+8;
        t = t%3600;
        int m = t/60;
        t = t%60;
        printf("%02d:%02d:%02d ", h, m, t);
        if(h>=12) printf("pm");//中午12点是pm,晚上12点是am
        else      printf("am");
        printf("\n");
    }
    return 0;
}
I - 最少拦截系统

题目链接:I - 最少拦截系统

  • 思路:类似于最长非增子序列,但是需要求非增子序列的最少个数,因此可以使用一个数组d来存储每一个非增子序列的末尾元素,如果当前元素小于等于其中的元素,则更新最接近它的元素,如果均小于它,则将该元素添加到数组中,最后数组的大小即为所求。

代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int MAX = 500000;
int a[MAX], dp[MAX];
vector<int> d;//记录每一个非增子序列的末尾元素,更新时一定是从小到大排好序
int main()
{
    int n;
    while(scanf("%d", &n)!=EOF)
    {
        d.clear();
        for(int i=1; i<=n; i++)
            scanf("%d", &a[i]);
        fill(dp, dp+n+1, 1);
        d.push_back(a[1]);
        for(int i=2; i<=n; i++)
        {
            int j;
            for(j=0; j<d.size(); j++)
            {
                if(d[j]>=a[i])//表明a[i]可以加入d[j]结尾的序列中,因此更新末尾元素
                {
                    d[j] = a[i];
                    break;
                }
            }
            if(j==d.size()) dp[i] = dp[i-1]+1, d.push_back(a[i]);//表明没有更新
            else            dp[i] = dp[i-1];
        }
        printf("%d\n", d.size());
    }
    return 0;
}
J - FatMouse’s Speed

题目链接:J - FatMouse’s Speed

  • 思路:保证按重量递增但是同时速度递减,则可以先按重量从小到大排序,然后就是对于速度的最大递减子序列,但是需要记录序列,可以使用pre数组记录路径。

代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;

struct Node
{
    int w, s, id;
    bool operator < (const Node &A) const
    {
        if(w==A.w) return s>A.s;
        else       return w<A.w;
    }
};
Node M[2000];
int pre[10005], dp[2000];//dp表示前i个的子序列长度

int main()
{
    int cnt = 1;
    while(scanf("%d%d", &M[cnt].w, &M[cnt].s)!=EOF)
    {
        M[cnt].id = cnt;
        cnt++;
    }
    sort(M+1, M+cnt);
    fill(dp, dp+cnt+1, 1);
    memset(pre, 0, sizeof(pre));
    for(int i=1; i<cnt; i++)
    {
        for(int j=1; j<i; j++)
        {
            if(M[j].w<M[i].w && M[j].s>M[i].s)//严格
            {
                if(dp[i]<dp[j]+1)
                {
                    dp[i] = dp[j]+1;
                    pre[i] = j;
                }
            }
        }
    }
    int Max = 0, idx;
    for(int i=1; i<cnt; i++)
    {
        if(Max<dp[i])
        {
            Max = dp[i];
            idx = i;//找出末尾下标
        }
    }
    vector<int> ans;
    ans.push_back(M[idx].id);//存储原始编号
    while(pre[idx])
    {
        idx = pre[idx];//反向寻找路径
        ans.push_back(M[idx].id);
    }
    printf("%d\n", Max);
    for(int i=ans.size()-1; i>=0; i--)
        printf("%d\n", ans[i]);
    return 0;
}
K - Jury Compromise

题目链接:K - Jury Compromise

  • 思路:
L - Common Subsequence

题目链接:L - Common Subsequence

  • 思路:裸的LCS。

代码:

#include 
#include 
#include 
using namespace std;

const int MAX = 1000;
int dp[MAX][MAX];
int main()
{
    string s1, s2;
    while(cin >> s1 >> s2)
    {
        int n = s1.size(), m = s2.size();
        s1 = " "+s1, s2 = " "+s2;
        memset(dp, 0, sizeof(dp));
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                if(s1[i]==s2[j])
                    dp[i][j] = dp[i-1][j-1]+1;
                else
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            }
        }
        printf("%d\n", dp[n][m]);
    }
    return 0;
}
M - Help Jimmy

题目链接:M - Help Jimmy
参考博文:M - Help Jimmy DP

  • 思路:好题。首先垂直高度一定为Y,不需要考虑,而水平方向需要考虑向左还是向右。
    首先将板子按高度从小到大排序,且起始点为高度为Y,长度为0的板子,则起始点一定是最后一个板子。
    • 状态构造:dp[i][0]表示在第i个板子上向左走(到地面)的水平时间,dp[i][1]表示在第i个板子上向右走(到地面)的水平时间。
    • 终态:dp[n][0],表示起始点的水平时间。
    • 状态转移方程:
      • d p [ i ] [ 0 ] = m i n ( d p [ l p ] [ 0 ] + a [ i ] . l − a [ l p ] . l , d p [ l p ] [ 1 ] + a [ l p ] . r − a [ i ] . l ) dp[i][0] = min(dp[lp][0]+a[i].l-a[lp].l, dp[lp][1]+a[lp].r-a[i].l) dp[i][0]=min(dp[lp][0]+a[i].la[lp].l,dp[lp][1]+a[lp].ra[i].l);lp表示从第i个板子左边掉落可以到达的板子的下标,a[i].l表示第i个板子的左边缘的水平坐标,a[i].r表示第i个板子的右边缘的水平坐标。
      • d p [ i ] [ 1 ] = m i n ( d p [ r p ] [ 0 ] + a [ i ] . r − a [ r p ] . l , d p [ r p ] [ 1 ] + a [ r p ] . r − a [ i ] . r ) dp[i][1] = min(dp[rp][0]+a[i].r-a[rp].l, dp[rp][1]+a[rp].r-a[i].r) dp[i][1]=min(dp[rp][0]+a[i].ra[rp].l,dp[rp][1]+a[rp].ra[i].r);
    • dp[][] = INF
      需要考虑高度是否可以落下,如果不可以则为INF。具体见代码

代码:

#include 
#include 
#include 
#include 
using namespace std;

const int INF = 1<<29;
const int MAX = 1005;

struct Node
{
    int l, r, h;
}a[MAX];
int dp[MAX][2];
bool cmp(Node a, Node b)
{
    return a.h<b.h;
}
void Init(int n)
{
    for(int i=0; i<=n+1; i++)
    {
        for(int j=0; j<2; j++)
            dp[i][j] = INF;
    }
}
int main()
{
    int t, n, x, y, d;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d%d", &n, &x, &y, &d);
        Init(n);
        for(int i=0; i<n; i++)
        {
            scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].h);
        }
        a[n].l = a[n].r = x;
        a[n].h = y;
        sort(a, a+n, cmp);//按高度从小到大
        for(int i=0; i<=n; i++)
        {
            int lp = -1, rp = -1;//分别为左边和右边板子的编号
            for(int j=i-1; j>=0; j--)//从高度比i低的板子中选择左右板子
            {
                if(lp==-1 && a[j].l<=a[i].l && a[j].r>=a[i].l)
                {
                    lp = j;
                }
                if(rp==-1 && a[j].r>=a[i].r && a[j].l<=a[i].r)
                {
                    rp = j;
                }
            }
            if(lp==-1)//当不存在左边的板子
            {
                if(a[i].h<=d) dp[i][0] = 0;//可以直接落到地上
                else          dp[i][0] = INF;//这边不可以落下
            }
            if(rp==-1)
            {
                if(a[i].h<=d) dp[i][1] = 0;
                else          dp[i][1] = INF;
            }
            if(lp!=-1 && a[i].h-a[lp].h<=d)//左边的板子可以落下
                dp[i][0] = min(dp[lp][0]+a[i].l-a[lp].l, dp[lp][1]+a[lp].r-a[i].l);
            if(rp!=-1 && a[i].h-a[rp].h<=d)
                dp[i][1] = min(dp[rp][0]+a[i].r-a[rp].l, dp[rp][1]+a[rp].r-a[i].r);
        }
        printf("%d\n", dp[n][0]+y);
    }
    return 0;
}
N - Longest Ordered Subsequence

题目链接:N - Longest Ordered Subsequence

  • 思路:裸的LIS。

代码:

#include 
#include 
#include 
#include 
using namespace std;

int a[10005], dp[10005];

int main()
{
    int N;
    scanf("%d", &N);
    fill(dp, dp+N+1, 1);
    int ans = 0;
    for(int i=1; i<=N; i++)
    {
        scanf("%d", &a[i]);
        for(int j=1; j<i; j++)
        {
            if(a[j]<a[i])
                dp[i] = max(dp[i], dp[j]+1);
        }
        if(ans<dp[i]) ans = dp[i];
    }
    printf("%d\n", ans);
    return 0;
}
O - Treats for the Cows

题目链接:O - Treats for the Cows
* 思路:一开始看了样例以为是贪心,但是有反例。实则是区间DP。区间DP一般循环时外层都是区间长度。
* 状态构造:dp[i][j]表示序列的第i个到第j个的最大价值。
* 终态:dp[1][N]。
* 状态转移方程: d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j ] + a [ i ] ∗ ( N − ( j − i ) ) , d p [ i ] [ j − 1 ] + a [ j ] ∗ ( N − ( j − i ) ) ) dp[i][j] = max(dp[i+1][j]+a[i]*(N-(j-i)), dp[i][j-1]+a[j]*(N-(j-i))) dp[i][j]=max(dp[i+1][j]+a[i](N(ji)),dp[i][j1]+a[j](N(ji))),表示取首部和取尾部的最大值。
* 初始化:dp[i][i] = a[i]*N。

代码:

#include 
#include 
#include 
#include 
using namespace std;

typedef long long LL;
const int MAX = 2005;
LL a[MAX], dp[MAX][MAX];

int main()
{
    int N;
    scanf("%d", &N);
    memset(dp, 0, sizeof(dp));
    for(int i=1; i<=N; i++)
    {
        scanf("%d", &a[i]);
        dp[i][i] = a[i]*N;
    }
    for(int len = 2; len<=N; len++)
    {
        for(int i=1; i<=N; i++)
        {
            int j=i+len-1;
            if(j>N) break;
            dp[i][j] = max(dp[i+1][j]+a[i]*(N-(j-i)), dp[i][j-1]+a[j]*(N-(j-i)));
        }
    }
    printf("%lld\n", dp[1][N]);
    return 0;
}
P - FatMouse and Cheese

题目链接:P - FatMouse and Cheese

  • 思路:注意看题意,是只能水平跑或垂直跑。类似于滑雪问题,只不过范围扩大了,依然可以使用记忆化搜索。dp[x][y]表示在(x,y)处可以吃到的最多的奶酪。

代码:

#include 
#include 
#include 
#include 
using namespace std;

int n, k;
int G[105][105];
int dp[105][105];
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};

int DP(int x, int y)
{
    int &ans = dp[x][y];
    if(ans!=-1) return ans;
    ans = G[x][y];
    for(int i=1; i<=k; i++)//步数
    {
        for(int j=0; j<=4; j++)//四个方向
        {
            int x1 = x+dx[j]*i, y1 = y+dy[j]*i;
            if(x1<0 || x1>=n || y1<0 || y1>=n || G[x1][y1]<=G[x][y]) continue;
            ans = max(ans, DP(x1, y1)+G[x][y]);//转移方程
        }
    }
    return ans;
}
int main()
{
    while(scanf("%d%d", &n, &k)!=EOF && n!=-1)
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                scanf("%d", &G[i][j]);
            }
        }
        memset(dp, -1, sizeof(dp));
        printf("%d\n", DP(0, 0));
    }
    return 0;
}
Q - Phalanx

题目链接:Q - Phalanx
参考博文:hdu 2859 (二维dp)

  • 思路:这道题的状态定义和转移方程构造比较巧妙,值得思考。
    • 状态构造:dp[i][j]表示以(i,j)为左下角的对称矩阵的边长。
    • 终态:max(dp[i][j])
    • 状态转移方程:
      • 当i!=0&&dp[i-1][j+1]>i-a时,dp[i][j]=dp[i-1][j+1]+1
      • 当i!=0&&dp[i-1][j+1]
      • 当i==0时,dp[i][j]=1;

代码:

#include 
#include 
#include 
using namespace std;

int n;
string G[1005];
int dp[1005][1005];//dp[i][j]表示(i, j)为矩阵的左下角

int main()
{
    while(cin >> n && n)
    {
        getchar();
        for(int i=0; i<n; i++)
            getline(cin, G[i]);
        int ans = 0, a, b, len;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                if(i==0) dp[i][j] = 1;
                else
                {
                    a = i, b = j;//a是行,b是列
                    while(G[a][j]==G[i][b])
                    {
                        a--, b++;
                        if(a<0 || b>=n) break;
                    }
                    len = i-a;//对称的长度
                    if(len>dp[i-1][j+1]) dp[i][j] = dp[i-1][j+1]+1;
                    else                 dp[i][j] = len;
                }
                if(ans<dp[i][j]) ans = dp[i][j];
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
R - Milking Time

题目链接:R - Milking Time

  • 思路:我采用了LIS的状态构造思想。将区间按照结束时间从小到大排列。
    • 状态构造:dp[i]表示以第i个区间为结束的可以获得的最大产量。
    • 终态:max(dp[i])。
    • 状态构造:dp[i] = max(dp[i], dp[j]+T[i].eff),其中j
    • 初始化:dp[i] = T[i].eff。

代码:

#include 
#include 
#include 
#include 
using namespace std;

const int MAX = 1005;
int N, M, R;
int dp[MAX];

struct Node
{
    int st, ed, eff;
    bool operator < (const Node &A) const
    {
        if(ed==A.ed) return st<A.st;
        else         return ed<A.ed;
    }
};
Node T[MAX];

int main()
{
    scanf("%d%d%d", &N, &M, &R);
    memset(dp, 0, sizeof(dp));
    for(int i=1; i<=M; i++)
    {
        scanf("%d%d%d", &T[i].st, &T[i].ed, &T[i].eff);
    }
    sort(T+1, T+M+1);
    for(int i=1; i<=M; i++)
        dp[i] = T[i].eff;
    int ans = dp[1];
    for(int i=2; i<=M; i++)
    {
        for(int j=1; j<i; j++)
        {
            if(T[j].ed+R<=T[i].st)
                dp[i] = max(dp[i], dp[j]+T[i].eff);
        }
        if(ans<dp[i]) ans = dp[i];
    }
    printf("%d\n", ans);
    return 0;
}
S - Making the Grade

题目链接:S - Making the Grade
参考博文:Making the Grade (线性+优化思维?)

  • 思路:不断的优化,挺难想。
    • 状态构造:dp[i][j]表示将第i个数变成第j小的数的最小代价,变完之后前i个数按序排列。
    • 终态:min(dp[n][j])
    • 状态转移方程:dp[i][j] = min(dp[i-1][k])+abs(a[i]-num[j]);(k<=j)其中a[i]表示第i个数的值,num[j]表示第j小的数的值。
    • 初始化:dp[][] = 0。
      重点是代码的优化,变成了O(n^2)。

代码:

#include
#include
#include
#include
#include
#include
using namespace std;

const int MAXN = 2005;
const int INF = 1<<30;//29位报错
int n;
int dp[MAXN],a[MAXN],b[MAXN],num[MAXN];

int get_ans(int a[], int num[])
{
    memset(dp, 0, sizeof(dp));
    for(int i=1; i<=n; i++)
    {
        int Min = INF;
        for(int j=1; j<=n; j++)//得到第i个数变成第j小的数的代价
        {
            Min = min(Min, dp[j]);//取第i-1个数变成第k(k<=j)小数的最小代价
            dp[j] = Min + abs(a[i]-num[j]);
        }
    }
    int ans = INF;
    for(int i=1; i<=n; i++) ans = min(ans, dp[i]);
    return ans;
}

int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d", &a[i]);
        num[i] = a[i];
        b[n-i+1] = a[i];//倒序
    }
    sort(num+1, num+n+1);
    printf("%d\n", min(get_ans(a, num), get_ans(b, num)));
    return 0;
}

你可能感兴趣的:(ACM刷题之路)