贪心算法
哈夫曼编码
单源点最短路径
最小生成树
活动安排问题
最优装载问题
单机调度问题
哈夫曼编码先统计频率,将频率最小两点combine然后重复以上步骤,直到形成一棵树,一般的编码习惯为左子树取0,右子树取1
单源点最短路径(Dijkstra算法*)
不使用ADT版
#include<iostream>
#define N 1000
#define INF 0x7fffffff
using namespace std;
bool visited[N+1]={0};//存储各结点是否访问过,索引从1开始
int map[N+1][N+1];//存储图的邻接表,从(1,1)开始
int Dist[N+1];//存放最短路径,索引从1开始
//dijkstra算法中的N与宏定义中的N含义不同,此处不执行代码,方便起见,用同一个字母
void dijkstra (int s) {//传入源点
int pos = -1;
for (int i = 1; i <= N;i++) {//该数组从1开始
Dist[i] = map[s][i];
}
visited[s] = 1;
for (int i = 1; i <= N;i++) {
int min = INF;
for (int j = 1; j <= N;j++) {//找未被访问的最小距离点
if(Dist[j]<min&&!visited[j]) {
min = Dist[j];
pos = j;
}
}
if(min==INF)//不联通点
break;
visited[pos] = 1;//设为已访问
for (int j = 1; j <= N;j++) {//更新未被访问点的最小距离
if(!visited[j]&&Dist[j]>Dist[pos]+map[pos][j])
Dist[j] = Dist[pos] + map[pos][j];
}
}
}
使用HNU的ADT版
void Dijkstra1(int* D, int s)
{
int v,w;
//注意,通常情况下D数据是需要对source进行初始化的,但这里不需要,D的初始化在main函数里做过了,要是再做的话,0会覆盖掉本该出现的INF
for(int i=0;i<G->n();i++) {//这个意思是说要遍历n个结点
v=minVertex(D);//找到D数组中为访问的最小数的下标,别的不用管,minVertex帮你做过了
if(v==INFINITY) return;
G->setMark(v,VISITED);
for(w=G->first(v);w<G->n();w=G->next(v,w)) //我这里还有疑问,为什么不需要判断w结点是否已访问过呢?我还试了下,如果验证是否访问,反而会出现错误
if(D[w]>D[v]+G->weight(v,w)) D[w]=D[v]+G->weight(v,w);
}
}
最小生成树(Prim算法*)
int Prim() {
int pos,min,sum=0;
memset(visited, 0, sizeof(visited));
for (int i = 1; i <= N; i++)
Dist[i] = map[1][i]; // Prim算法从哪个点出发都是一样的
visited[1] = 1;
for (int i = 1; i <= N;i++) {
min = INF;
for (int j = 1; j <= N;j++) {
if(!visited[j]&&Dist[j]<min) {
min = Dist[j];
pos = j;
}
}
if(min==INF)
break;
visited[pos]=1;
for (int j = 1; j <= N;j++) {
if(!visited[j]&&Dist[j]>map[pos][j])
Dist[j] = map[pos][j];
}
}
for (int i = 1;i<=N;i++) {//这里这一步是算最小生成树的各边权重和,可以没有
sum += Dist[i];
if(Dist[i]==INF)
return -1;
}
return sum;
}
HNU的ADT版本
int V[G->n()];
int v,w,i;
for(int i=0;i<G->n();i++) {
v=minVertex(D);
if(v==INFINITY) return;
G->setMark(v,VISITED);
if(v!=s) ADDEdgetoMST(V[v],v);
for(w=G->first(v);w<G->n();v=G->next(v,w))
if(D[w]>G->weight(v,w)) {
D[w]=G->weight(v,w);
V[w]=v;
}
}
}
结构差不太多,一通百通
最小生成树(kruskal算法)
涉及归并
来源leetcode官方题解
int Find(vector<int>& parent,int index) {
if(parent[index]!=index) {//当前结点有父节点
parent[index] = Find(parent, parent[index]);//找爷爷结点
}
return parent[index];//当前结点没有父节点,返回
}
void union(vector<int>& parent,int index1,int index2) {//归并两个连通分量
parent[Find(parent, index1)] = Find(parent, index2);
}
vector<int> findRedundantConnection(vector<vector<int> >&edges) {
int n=edges.size();
vector<int> parent(n+1);
for(int i=1;i<=n;++i) {
parent[i] = i;//先初始化为每个结点都未连通
}
for(auto& edge:edges) {
int node1=edge[0],node2=edge[1];
if(Find(parent,node1)!=Find(parent,node2)) {
Union(parent, node1, node2);
}
else {
return edge;
}
}
return vector<int>{};
}
符号三角形*(回溯算法)
下图是由14个“+”和14个“-”组成的符号三角形。2个同号下面都是“+”,2个异号下面都是“-”。
在一般情况下,符号三角形的第一行有n个符号。符号三角形问题要求对于给定的n,计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。
符号三角形斜着算的,具体看代码
这里贴两种代码,一个是老师给的标准模板(虽然跑不出来正确答案)
int sum = 0;//放结果,三角形个数
int p[1000][1000];//放符号三角形本身,开大数组
int count=0;//统计符号个数
int n;//三角形层数
int half = (n+1) * n / 4;//符号三角形的一半应该有这个数
void Backtrack(int t)
{
if ((count>half)||(t*(t-1)/2-count>half)) return;//不符合条件剪枝剪掉
if (t>n) sum++;//能走到这里必然是满足条件的
else
for (int i=0;i<2;i++) {
p[1][t]=i;
count+=i;
for (int j=2;j<=t;j++) {
p[j][t-j+1]=p[j-1][t-j+1]^p[j-1][t-j+2];
count+=p[j][t-j+1];
}
Backtrack(t+1);
for (int j=2;j<=t;j++)
count-=p[j][t-j+1];
count-=i;
}
}
int main() {
cin >> n;
Backtrack(0);
cout << sum;
return 0;
}
另一个版本,可以跑通的代码,借鉴CSDN上大佬的,漂亮且简洁,最重要的是,可以跑通的!不同在于递归终止条件和剪枝的匹配。
#include<cstdio>
using namespace std;
int n;
int half;
int sum;
int count; //1代表+ ; 0代表- ; count就是+的计数
int p[100][100];
void Dfs(int t);
int main()
{
scanf("%d",&n);
if((n+1) * n % 4)
{
printf("ERROR");
return 0 ;
}
half = (n+1) * n / 4; // + 和 - 都不能超过half
sum = 0; //答案数初始化;
count = 0;
Dfs(0);
printf("%d",sum);
return 0;
}
void Dfs(int t)//第t步,也就是第一行的第t位
{
if(t == n && count == half)
{
sum ++;
return ; //得到一个答案,向上return 回溯
}
//如果还有位子,一个位子要尝试 + - 两种情况
for(int i = 0; i < 2; i++)
{
p[0][t] = i;
count += i;
for(int j = 1; j <= t; j++)
{
p[j][t-j] = p[j-1][t-j]^p[j-1][t-j+1];
count += p[j][t-j];
}
if(count <= half && (t+2)*(t+1)/2 - count <= half)//当前图形中+ -都没超过一半
{
Dfs(t + 1);
}
//还原本次,考虑下一个符号
for( int j = 1; j <= t; j++)
{
count -= p[j][t-j];
}
count -= i;
}
}
八皇后问题(回溯算法*)
还是上两个版本,一个是课上写过的标准版本,一个是CSDN上大神的版本
大神版
bool place(int k)
{
for(int j = 1;j<k;j++)
if(abs(x[k] - x[j]) == abs(k-j)||x[j] == x[k])//皇后在对角线上或者竖线上
return false;//返回不能放
return true;
}
void backtrack(int t)
{
if(t>num) //num为皇后的数目
{
sum++;//sum为所有的可行的解
for(int m = 1;m<=num;m++)
{
cout<<"<"<<m<<","<<x[m]<<">";//这一行用输出当递归到叶节点的时候,一个可行解
}
cout<<endl;
}
else
for(int i = 1;i<=num;i++)
{
x[t] = i;
if(place(t))
backtrack(t+1);//此处的place函数用来进行我们上面所说的条件的判断,如果成立,进入下一级递归
}
}