九度OJ小结2

时间:2024-08-12 18:06:44

由于安排问题,距离上次小结时间已经过去很久。导致这次小结的内容很多。

本次小结涉及到主要内容如下所示:

  • 基于并查集操作的最小生成树问题(prime算法或者kruskal算法);
  • 最短路径问题(Floyd算法和dijkstra算法);
  • 有向无环图问题(拓扑排序问题);
  • 广度优先搜素BFS(Breadth First Search);
  • 深度优先搜索DFS(Depth First Search);
  • 递归问题;
  • 回溯法;
  • 递推算法;
  • 最长递增子序列问题(一个数组LIS longest increasing subsequence);
  • 最长公共公子序列问题(两个字符串 LCS longest common subsequence);
  • 动态规划问题;
  • 背包问题(0-1背包、0-1背包改进版、完全背包、多重背包) ;
  • 字符串查找操作。

1. 最小生成树问题

  对于最小生成树问题一般有两种求解方法:分别为prime算法和kruskal算法。

  prime算法:  

int prime(int cur)
{
int index = cur;
int sum = ;
memset(visit, false, sizeof(visit));
visit[cur] = true;
for(int i = ; i < m; i ++){
dist[i] = graph[cur][i];
} for(int i = ; i < m; i ++){ int mincost = INF;
for(int j = ; j < m; j ++){
if(!visit[j] && dist[j] < mincost){
mincost = dist[j];
index = j;
}
} visit[index] = true;
sum += mincost; for(int j = ; j < m; j ++){
if(!visit[j] && dist[j] > graph[index][j]){
dist[j] = graph[index][j];
}
}
}
return sum;
}

  kruskal算法:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <cmath>
#define MAX_SIZE 1010 using namespace std; int n, m;
int Tree[MAX_SIZE]; int findRoot(int x){//find the root of x
if(Tree[x]==-) return x;
else{
int tmp = findRoot(Tree[x]);//continue the find
Tree[x] = tmp;//change the root of x to tmp
return tmp;
} }
int main(){
while(scanf("%d",&n)!=EOF && n!=){//when n==0 and jump out of the loop
scanf("%d",&m);
for(int i = ; i <= n ; i++)//init
Tree[i] = -; while(m--){
int a,b;
scanf("%d%d",&a,&b);
a = findRoot(a);//find the root of a
b = findRoot(b);//find the root of b
if(a!=b) Tree[a]=b;//merge those two sets to the same aggregates
} int ans = ;
for(int i = ; i <= n ; i++){
if(Tree[i]==-) ans++;//calculate the total numbers of aggregates
}
printf("%d\n",ans-);
}
return ;
}

2. 最短路径问题

  对于最短路径问题一般使用两种算法Floyd算法和

  Floyd算法:

void floyd()
{
for(int k = ; k < n; k ++){ //作为循环中间点的k必须放在最外一层循环
for(int i = ; i < n; i ++){
for(int j = ; j < n; j ++){
if(dist[i][j] > dist[i][k] + dist[k][j]){
dist[i][j] = dist[i][k] + dist[k][j]; //dist[i][j]得出的是i到j的最短路径
}
}
}
}
}

  Dijkstra算法

void dijkstra(int s)   //s是起点
{
memset(visit, false, sizeof(visit));
visit[s] = true;
for(int i = ; i < n; i ++){
dist[i] = graph[s][i];
} int index;
for(int i = ; i < n; i ++){
int mincost = INF;
for(int j = ; j < n; j ++){
if(!visit[j] && dist[j] < mincost){
mincost = dist[j];
index = j;
}
}
visit[index] = true;
for(int j = ; j < n; j ++){
if(!visit[j] && dist[j] > dist[index] + graph[index][j]){
dist[j] = dist[index] + graph[index][j];
}
}
}
}

最小生成树和最短路径的详细讲解以及算法实现代码见:http://www.cnblogs.com/zpfbuaa/p/6732997.html

3. 有向无环图问题

  对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

  可参考题目:

        题目链接:http://ac.jobdu.com/problem.php?pid=1448  

        题目链接:http://ac.jobdu.com/problem.php?pid=1449

    题目链接:http://ac.jobdu.com/problem.php?pid=1450

  拓扑排序操作:

  1. 首先,所有有入度(即以该结点为弧头的弧的个数)的结点不可能排在第一个。那么,我们选择一个入度为 0 的结点,作为序列的第一个结点。当该结点被选为序列的第一个顶点后,我们将该点从图中删去,同时删去以该结点为弧尾的所有边,得到一个新图。

  2. 那么这个新图的拓扑序列即为原图的拓扑序列中除去第一个结点后剩余的序列。同样的,我们在新图上选择一个入度为0的结点,将其作为原图的第二个结点,并在新图中删去该点以及以该点为弧尾的边。这样我们就得到了一个新图,重复同样的方法,直到所有的结点和边都从原图中删去。

  3. 若在所有结点尚未被删去时即出现了找不到入度为0的结点的情况,则说明剩余的结点形成一个环路,拓扑排序失败,原图不存在拓扑序列。

4. 广度优先搜索BFS

  使用队列的先进先出特性,对每一条路径组成的树进行全部遍历。BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。因此BFS适用于求解最优问题,而下面的深度优先搜索则适用于寻找问题是否有解。

  可参考题目:

     题目链接:http://ac.jobdu.com/problem.php?pid=1456

     题目链接:http://ac.jobdu.com/problem.php?pid=1457    

5. 深度优先搜索DFS

 深度优先遍历图的方法是,从图中某顶点v出发:

  (1)访问顶点v;
  (2)依次从v的未被访问的邻接点出发,对图进行DFS;直至图中和v有路径相通的顶点都被访问;
  (3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行DFS,直到图中所有顶点均被访问过为止。
  可参考题目:
6. 递归问题
  递归算法是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数来表示问题的解。一个函数直接或间接调用自己本身,这种函数叫递归函数,其中若区分函数和过程则上述的函数均可替换为过程。递归就是程序自己调用自己( recursion)

  一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

  递归算法所体现的“重复”一般有三个要求:
  1. 每次调用在规模上都有所缩小(通常是减半);
  2. 相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
  3. 在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。
  可参考题目:
    题目链接:http://ac.jobdu.com/problem.php?pid=1460

 7. 回溯法

  回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

  在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。

若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。

而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

8. 最长递增子序列问题

  常见的dp问题.

  九度OJ小结2

  

for(int i =  ; i < n ; i++){
for(int j = ; j < i ; j++){
if(height[i]>height[j]){
l2r[i]=max(l2r[i],l2r[j]+);
}
}
}

  可参考题目:

  题目链接:http://ac.jobdu.com/problem.php?pid=1112

  题目链接:http://ac.jobdu.com/problem.php?pid=1131

9. 最长公共公子序列问题

  九度OJ小结2

  可参考题目:

  题目链接:http://ac.jobdu.com/problem.php?pid=1042

10. 动态规划问题

  推荐博客:http://www.cnblogs.com/wuyuegb2312/p/3281264.html

  可参考题目:

    题目链接:http://ac.jobdu.com/problem.php?pid=1452

    题目链接:http://ac.jobdu.com/problem.php?pid=1453

11. 背包问题

  背包九讲:http://www.cnblogs.com/zpfbuaa/p/4964317.html 

  0-1背包修改版:http://www.cnblogs.com/zpfbuaa/p/4966349.html 

  0-1背包再修改版:http://www.cnblogs.com/zpfbuaa/p/4966387.html 

  多重背包:http://www.cnblogs.com/zpfbuaa/p/4991895.html

  完全背包:题目链接:http://ac.jobdu.com/problem.php?pid=1454

  多重背包:题目链接:http://ac.jobdu.com/problem.php?pid=1455

12. 字符串操作

  string转为char数组,str.c_str()

  string中的替换:str.replace(startPos,replaceLegth,insertStr);

  string中的查找:str.find(findSrtr,startPos) (返回值为第一次出现的下标)

  string中的擦除:

  (1)erase(pos,n); 删除从pos开始的n个字符,比如erase(0,1)就是删除第一个字符
  (2)erase(position);删除position处的一个字符(position是个string类型的迭代器)
  (3)erase(first,last);删除从first到last之间的字符(first和last都是迭代器)
 其他操作转博客:http://blog.****.net/hanshileiai/article/details/6691755