求数组中最长递增子序列,数组分割,数组循环移位,区间重合判断

求数组中最长递增子序列

编程之美2.16
注意,所谓的递增子序列不一定是连续的
只要满足递增性即可。
方法:动态规划思想。
用dp[i]表示到i为止,最长的递增序列长度。(可能包含i,也可能不包含i)
那么dp[i+1]就是判断A[i+1]能否加上,即
dp[i+1]=max(dp[0~i]+A[0~i]

int calculong(int *A, int n)
{
    int *dp = new int[n];
    dp[0] = 1; 
    for (int i = 1;i < n;i++)
    {
        int tmp = INT_MIN;
        for (int j = 0;j < i;j++)
        {
            if (A[i] > A[j] && tmp < dp[j] + 1)
                tmp = dp[j] + 1;
            else if (tmp<dp[j])
                tmp = dp[j];
        }
        dp[i] = max(1, tmp);
    }
    int res = INT_MIN;
    for (int i = 0;i < n;i++)
        if (res < dp[i])
            res = dp[i];
    return res;
}

数组分割

对于一个无序数组,长度2n,如何把这个数组分割为两个长度为n的数组,使其和最接近。

先解决这样一个问题:
//1. 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为两个子数组,子数组的元素个数不限,并使两个子数组之和最接近。

#define MAXN 101  
#define MAXSUM 1000  
void ArrayDivision(int *A, int n)
{
    bool dp[MAXN][MAXSUM];// dp[k][s]表示从前k个数中取任意个数的数,且这些数之和为s的取法是否存在  
    memset(dp, 0, sizeof(dp));
    dp[0][0] = true;
    int i, sum=0;
    for (i = 0; i <= 2 * n-1; i++)
        sum += A[i];
    cout << sum << endl;
    // 外阶段k1表示第k1个数
    for (int k1 = 1;k1 <= 2 * n;k1++)
            for (int s = 1;s <= sum / 2;s++)
            {
                // 有两个决策包含或不包含元素k1
                if (dp[k1 - 1][s])
                    dp[k1][s] = true;
                if (s >= A[k1-1] && dp[k1 - 1][s - A[k1-1]])
                    dp[k1][s] = true;
            }
    for (int s = sum / 2;s >= 1;s--)
    {
        if (dp[2 * n][s])
        {
            cout << s << endl;
            break;
        }
    }

}

再回到原问题:

//2. 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组之和最接近。
void ArrayDivision2(int *A, int n)
{
    bool dp[MAXN][MAXSUM];// dp[k][s]表示是否可以找到k个数,其和为s 
    memset(dp, 0, sizeof(dp));
    dp[0][0] = true;
    int i, sum = 0;
    for (i = 0; i <= 2 * n - 1; i++)
        sum += A[i];
    cout << sum << endl;
    // 外阶段k1表示第k1个数
    for (int k1 = 1;k1 <= 2 * n;k1++)
        for (int k2 = min(k1, n);k2 >= 1;k2--)
            for (int s = 1;s <= sum / 2;s++)
            {
                if (s >= A[k1 - 1] && dp[k2 - 1][s - A[k1 - 1]])//因为每次更新k1,我们就要判断新增加的A[k1]是否会影响前面的dp[0~k2][s]的值,所以只需更新加上A[k1]后影响的值就行了。
                    dp[k2][s] = true;
            }

    for (int s = sum / 2;s >= 1;s--)
    {
        if (dp[n][s])
        {
            cout << s << endl;
            break;
        }
    }

}

第二种不太好想,看注释!

注意两个dp[k][s]表示的意思是不一样的。

数组循环移位

对数组循环向右移动k位

求解思路:
逆序前n-k个
逆序后k个
然后对整个逆序

区间重合判断

类似于阿里的一个面试题。
就是给你一个target线段,判断它是否被已给出的一些线段所包含。
思路:
先排序,然后遍历排序好的线段,对target的x进行更新判断。画图比划一下即可。

struct line
{
    int x;
    int y;
};
void swap(line &x, line &y)
{
    line temp=x;
    x = y;
    y = temp;
}
void QuickSort(line *A,int left, int right)
{
    if (left < right)
    {
        int lo = left, hi = right;
        line key = A[lo];
        while (lo < hi)
        {
            while (lo<hi&&A[hi].x >= key.x) hi--;
            swap(A[lo], A[hi]);
            while (lo < hi&&A[lo].x <= key.x) lo++;
            swap(A[lo], A[hi]);
        }
        QuickSort(A, left, lo-1);
        QuickSort(A, lo+1, right);
    }
}
bool IsExist(line*A, int n,line target)
{
    QuickSort(A,0,n-1);
    for (int i = 0;i < n;i++)
    {
        if (target.x >= target.y)
            return true;
        if (A[i].y < target.x)
        {
            continue;
        }
        else
        {
            if (A[i].x > target.x)
                continue;
            else
                target.x= A[i].y;
        }
    }
    return false;

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值