dijkstra(单源最短路径):
策略:
设集合S存放已被访问的顶点,然后执行n次下面的两个步骤
(1)每次从集合V-S(未访问节点)选择与起点s的距离最小的一个顶点(记为u),访问并加入集合S(已访问节点)。
(2)之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。
constint MAXV = 1000;//最大顶点数 constint INF = 0x3f3f3f3f;//设INF为一个很大的数 int n, G[MAXV][MAXV];//n为定点数,MAXV为最大定点数 int d[MAXV];//起点到各点的最短路径长度 bool vis[MAXV] = {false};//标记数组,vis[i]==true表示已访问。初值均为false void Dijkstra(int s){//s为起点 fill(d, d + MAXV, INF);//fill函数将整个d数组赋为INF(慎用memset) d[s] = 0;//起点s到达自身的距离为0 for(int i = 0; i < n; i++){//循环n次 int u = -1, MIN = INF;//u使d[u]最小,MIN存放该最小的d[u] for(int j = 0; j < n; j++){// if(vis[j] == false && d[j] < MIN){ u = j; MIN = d[j]; } } //找不到小于INF的d[u],说明剩下的顶点和起点s不连通 if(u == -1) return; vis[u] = true;//标记u为已访问 for(int v = 0; v < n; v++){//从u出发能达到的所有顶点v,更新d[v] //如果v未访问&&u能到达v&&以u为中介点可以使d[v]更优 if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){ d[v] = d[u] + G[u][v];//优化d[v] } } } }
图的DFS(求联通分量数):
①邻接矩阵
const int MAXV = 1000;//最大顶点数 const int INF = 0x3f3f3f3f;//设INF为一个很大的数 int n, G[MAXV][MAXV];//n为定点数,MAXV为最大定点数 bool vis[MAXV] = {false};//如果顶点i已被访问,则vis[i]==true。初值为false void DFS(int u, int depth) {//u为当前访问的顶点标号,depth为深度 vis[u] = true;//设置u已被访问 //如果需要对u进行一些操作,可以在这里进行 //下面对所有从u出发能到达的分支进行枚举 for (int v = 0; v < n; v++) {//对每个顶点v if (vis[v] == false && G[u][v] != INF) {//如果v未被访问,且u可达v DFS(v, depth + 1);//访问v,深度加1 } } } void DFSTrace(){//遍历图G for(int u = 0; u < n; u++){//对每个顶点u //int count=0//用来统计联通分量数 if(vis[u] == false){//如果u未被访问 DFS(u, 1);//访问u和所在的连通块,1表示初始为1层 //count++; } } }
②邻接表
vector<int> Adj[MAXV];//图G的邻接表 int n;//n为定点数,MAXV为最大顶点数 bool vis[MAXV] = { false };//如果顶点i已被访问,则vis[i]==true,初值为fales void DFS(int u, int depth) {//u为当前访问的顶点标号,depth为深度 vis[u] = true;//设置u已被访问 /*如果需要对u进行一些操作,可以在此处进行*/ for (int i = 0; i < Adj[u].size(); i++) {//对从u出发可以到达的所有顶线v int v = Adj[u][i]; if (vis[v] == false) {//如果v未被访问 DFS(v, depth + 1);//访问v,深度加1 } } } void DFSTrave() {//遍历图G for (int u = 0; u < n; u++) {//对每个顶点u if (vis[u] == false) {//如果u未被访问 DFS(u, 1);//访问u和u所在的连通块,1表示初始为第一层 } } }
图的BFS
int n, G[MAXV][MAXV];//n为顶点数,MAXV为最大顶点数 bool inq[MAXV] = { false };//若顶点i曾入过队列,则inq[i]==true,初值为false void BFS(int u) {//遍历u所在的连通块 queue<int> q;//定义队列q q.push(u);//将初始点u入队 inq[u] = true;//设置u已被加入过队列 while (!q.empty()) {//只要队列非空 int u = q.front();//取出队首元素 q.pop();//将队首元素出队 for (int v = 0; v < n; v++) { //如果u的邻接点v未曾加入过队列 if (inq[v] == false && G[u][v] != INF) { q.push(v);//将v入队 inq[v] = true;//标记v已被加入过队列 } } } } void BFSTrave() {//遍历图G for (int u = 0; u < n; u++) {//枚举所有顶点 if (inq[u] == false) {//如果u未曾加入过队列 BFS(u);//遍历u所在的连通块 } } }
01背包:
递推:
dp[i][v]表示前i件物品(1≤i≤n,0≤v≤V)恰好装入容量为v的背包中所能获得的最大价值。
考虑对i件物品的选择策略:
1、不放第i件物品,那么问题转化为前i-1件物品恰好装入容量为v的背包中所能获得 的最大价值,也即dp[i-1][v]
2、放第i件物品,那么问题转化为前i-1件物品恰好装入容量为v-w[i]的背包中所能获得的最大价值,也即dp[i-1][v-w[i]]+c[i]
$$ dp[i][v]=max\left\{dp[i-1][v],dp[i-1][v-w[i]]+c[i]\right\}$$
$$(1\le i \le n ,w[i]\le v\le V)$$
滚动数组:
$$ dp[j]=max\left\{dp[j],dp[j-w[i]]+c[j]\right\} $$
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 100;//物品最大件数 const int maxv = 1000;//V的上限 int w[maxn], c[maxn], dp[maxv]; int main() { int n, V; cin >> n >> V; for (int i = 0; i < n; i++) { cin >> c[i]; } //边界 for (int v = 0; v < V; v++) { dp[v] = 0; } for (int i = 1; i <= n; i++) { for (int v = V; v >= w[i]; v--) { //状态转移方程 dp[v] = max(dp[v], dp[v - w[i]] + c[i]); } } //寻找dp[0...V]中最大的即为答案 int max = 0; for (int v = 0; v <= V; v++) { if (dp[V] > max) { max = dp[v]; } } cout << max; }
完全背包
区别于01背包,每种物品都有无穷件。
dp[i][v]表示前i件物品(1≤i≤n,0≤v≤V)恰好装入容量为v的背包中所能获得的最大价值。
递推:
考虑对i件物品的选择策略:
1、不放第i件物品,那么dp[i][v]=dp[i-1][v],这步和01背包是一样的。
2、放第i件物品,不同于01背包,而是转移到dp[i][v-w[i]],这是因为每种物品可以放任意件(注意有容量限制,因此还是有限的),放了第i件物品后还可以继续放第i件物品,直到第二维的v-w[i]无法保持大于等于0为止。
$$ dp[i][v]=max\left\{dp[i-1][v],dp[i][v-w[i]]+c[i]\right\}$$
$$(1\le i \le n ,w[i]\le v\le V)$$
边界:$dp[0][v]=0(0\le v\le V)$
状态转移方程:
$$ dp[j]=max\left\{dp[j],dp[j-w[i]]+c[j]\right\} $$
形成一维后和01背包完全相同,唯一的区别在于这里v的枚举顺序是正向枚举。
最小生成树:
prim算法:
using namespace std; const int MAXV = 1000;//最大顶点数 const int INF = 100000000;//设INF为一个很大的数 int n, G[MAXV][MAXV];//n为顶点数,MAXV为最大顶点数 int d[MAXV];//顶点与集合S的最短距离 bool vis[MAXV] = { false };//标记数组,vis[i]==true表示已访问。初值均为false int prim() {//默认0号为初始点,函数返回最小生成树的边权之和 fill(d, d + MAXV, INF);//fill函数将整个d数组赋为INF(慎用memset) d[0] = 0;//只有0号顶点到集合S的距离为0,其余全为INF int ans = 0;//存放最小生成树的边权之和 for (int i = 0; i < n; i++) {//循环n次 int u = -1, MIN = INF;//u使d[u]最小,MIN存放该最小的d[u] for (int j = 0; j < n; j++) {//找到未访问的顶点中的d[]最小的 if (vis[j] == false && d[j] < MIN) { u = j; MIN = d[j]; } } //找不到小于INF的d[u],则剩下的顶点和集合S不连通 if (u == -1)return -1; vis[u] = true;//标记u为已访问 ans += d[u];//将与集合S距离最小的边加入最小生成树 for (int v = 0; v < n; v++) { //v未访问&&u能到达v&&以u为中介点可以使v离集合S更近 if (vis[v] == false && G[u][v] != INF && G[u][v] < d[v]) { d[v] = G[u][v];//将G[u][v]赋值给d[v] } } } return ans;//返回最小生成树的边权之和 }
最新评论