动态规划笔记(四)

最长公共子序列

  • 如何暴力回溯递归
int search(int xi,int yi){
    if(xi > n || yi > m){
        return -1;
    }
    if(xi == n && yi == m){
        return 0;
    }
    int a=0,b=0,c=0;
    if(X[xi] = Y[yi]){
     a= search(xi+1,yi+1)+1;   
    }
    b= search(xi,yi+1);
    c= search(xi+1,yi);
    return max(a,b,c);
}
  • 记忆化搜索
int search(int xi,int yi){
    if(xi > n || yi > m){
        return -1;
    }
    if(xi == n && yi == m){
        return 0;
    }
    if(dp[xi][yi]>=0){
        return dp[xi][yi];
    }
    int a=0,b=0,c=0;
    if(X[xi] = Y[yi]){
     a= search(xi+1,yi+1)+1;   
    }
    b= search(xi,yi+1);
    c= search(xi+1,yi);
    dp[xi][yi] = max(a,b,c);
    return dp[xi][yi];
}

旅行商问题

一个商人要不重复地访问N给城市,允许从任意城市出发,在任意城市结束。现已知任意两个城市之间的道路长度。

  • 求城市的访问序列,使得商人走过的路程最短
  • 例:N=4,访问序列3,4,1,2
  • NP问题,最优解无多项式时间算法
  • 时间复杂度?空间复杂度?
  • 状态压缩
    • 时间复杂度
    • 空间复杂度
map[i][j] //i到j距离
bool vis[n] = {false};
int search(int idx,int count){
    if(count==n){
        return 0;
    }
    int min = 1e8;
    for(int i=0;i<n;i++){
        if(!vis[i]){
            vis[i] = true;
            int t = search(i,count+1) + map[idx][i];
            if(t<min){
                min = t;
            }
            vis[i] = false; //复原,假装你没有访问过i(因为下一步i++,访问的就是i+1了)
        }
    }
    return min;
}

时间复杂度: O ( n ! ) O(n!) 跟全排列差不多

但是上面的代码要怎么优化呢?

注意:所有随着递归会变化的东西都需要放在函数的参数里面。

map[i][j] //i到j距离
bool vis[n] = {false};
int search(int idx,bool vis[]){
    if(vis中true的数量为n){
        return 0;
    }
    if(dp[idx][vis]算过){ //怎么存这个状态呢?用Map<Pair<int,bool[]>>
        return dp[idx][vis];
    }
    int min = 1e8;
    for(int i=0;i<n;i++){
        if(!vis[i]){
            vis[i] = true;
            int t = search(i,vis) + map[idx][i];
            if(t<min){
                min = t;
            }
            vis[i] = false; //复原,假装你没有访问过i(因为下一步i++,访问的就是i+1了)
        }
    }
    return min;
}
  1. 原来时间复杂度为 O ( n ! ) O(n!) (暴力搜索)
  2. 增加了 O ( n 2 n ) O(n*2^n) 的空间后,新的时间复杂度为 O ( n 2 2 n ) O(n^2*2^n)
    可以看出确实减少了冗余。

Tips

状态压缩:把数组压缩为一个整数

bool vis[4] = {false,true,true,false};
可以用int类型数组来存:
int vis[4] = {0,1,1,0};
再想想,还可以用字符串来存储呀!
string s = "0110";
最后!这不就是一连串二进制数么:
int res = (0110)2 = 6(10) 转换为10进制就OK啦!

也可以节省空间啦!原来sizeof(vis[4])可能等于4,16。。。
但是转换成int类型就是4个字节啦!

总结

  • 滚动数组,状态压缩,升维,单调性,四边形不等式(高级套路)
  • 本质:先暴力,找冗余,去冗余

猜你喜欢

转载自blog.csdn.net/qq_39504764/article/details/89956556
今日推荐