最长公共子序列
- 如何暴力回溯递归
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;
}
时间复杂度
:
跟全排列差不多
但是上面的代码要怎么优化呢?
注意:所有随着递归会变化的东西都需要放在函数的参数里面。
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;
}
- 原来时间复杂度为 (暴力搜索)
- 增加了 的空间后,新的时间复杂度为 。
可以看出确实减少了冗余。
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个字节啦!
总结
- 滚动数组,状态压缩,升维,单调性,四边形不等式(高级套路)
- 本质:先暴力,找冗余,去冗余