acwing提高班-动态规划2

acwing提高班-动态规划2

最长上升子序列问题

友好城市

#include 
using namespace std;
const int N = 5010;
int dp[N];
typedef pair<int, int> PP;
PP a[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 0;i<n;i++)scanf("%d %d",&a[i].first,&a[i].second);
    sort(a,a+n);
    for(int i = 0;i<n;i++){
        dp[i] = 1;
        for(int j = 0;j<i;j++){
            if(a[i].second > a[j].second){
                dp[i] = max(dp[i],dp[j]+1);
            }
        }
    }
    int res = 1;
    for(int i = 0 ;i<n;i++)res = max(res,dp[i]);
    printf("%d\n",res);
    return 0;
}

先排序,把问题转化为最长上升子序列问题。
状态都是以i结尾的最i上升子序列或者以i结尾的最长上升子序列和

导弹防御系统

#include 
using namespace std;
const int N = 55;
int w[N];
int up[N],down[N];

int ans;
int n;

void dfs(int now,int nu,int nd){
    if(nu + nd >= ans)return;
    if(now == n){
        ans = nu + nd;
        return;
    }
    // 先把now加入到上升子序列中
    int k = 0;
    while (k<nu && up[k] > w[now])k++;
    int t = up[k];
    up[k] = w[now];
    if(k<nu)dfs(now+1,nu,nd);
    else{
        dfs(now+1,nu+1,nd);
    }
    up[k] = t;

    // 把now加入到下降子序列中
    k = 0;
    while (k<nd && down[k] < w[now])k++;
    t = down[k];
    down[k] = w[now];
    if(k<nd)dfs(now+1,nu,nd);
    else{
        dfs(now+1,nu,nd+1);
    }
    down[k] = t;
}


int main()
{
    while (cin>>n,n){
        for(int i = 0;i<n;i++)cin>>w[i];
        ans = n;
        dfs(0,0,0);
        cout<<ans<<endl;
    }
    return 0;
}

思路是LIS 加上 dfs,dfs求最小值的方法是使用全局最小化或迭代加深,两者均可。不使用bfs是因为bfs一般会有状态爆炸,占用过多内存,且不易于剪枝。

最长公共上升子序列

#include 
using namespace std;
const int N = 3010;

int n;
int a[N],b[N];
int dp[N][N];
int main()
{
    scanf("%d",&n);
    for(int i = 1;i<=n;i++)scanf("%d",&a[i]);
    for(int i = 1;i<=n;i++)scanf("%d",&b[i]);

    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=n;j++){
            dp[i][j] = dp[i-1][j];
            if(a[i] == b[j]){
                dp[i][j] = max(dp[i][j],1);
                for(int k = 1;k<j;k++){
                    if(b[k] < b[j]){
                        dp[i][j] = max(dp[i][j],dp[i-1][k]+1);
                    }
                }
            }
        }
    }
    int res = 0;
    for(int i = 1;i<=n;i++)res = max(res,dp[n][i]);
    printf("%d\n",res);
    return 0;
}

思路是将最长上升子序列和最长公共子序列结合起来,dp[i][j] 表示的是以b[j]结尾的和a[:i]的最长公共上升子序列长度最大值

优化是在代码层面做等价变形

#include 
using namespace std;
const int N = 3010;

int n;
int a[N],b[N];
int dp[N][N];
int main()
{
    scanf("%d",&n);
    for(int i = 1;i<=n;i++)scanf("%d",&a[i]);
    for(int i = 1;i<=n;i++)scanf("%d",&b[i]);

    for(int i = 1;i<=n;i++){
        int maxv = 1;
        for(int j = 1;j<=n;j++){
            dp[i][j] = dp[i-1][j];
            if(a[i] == b[j]) dp[i][j] = max(maxv,dp[i][j]);
            if(b[j] < a[i]) maxv = max(dp[i][j]+1,maxv);
        }
    }
    int res = 0;
    for(int i = 1;i<=n;i++)res = max(res,dp[n][i]);
    printf("%d\n",res);
    return 0;
}

记录1-j 中 满足 b[k]

你可能感兴趣的:(算法刷题)