1.贪心算法
基本思想:贪心算法分阶段工作,在每一阶段,可以认为所做的决定是好的,而不考虑将来的后果。意味着选择的是局部最优,如果刚好是全局最优则算法正确,否则得到的是一个次优解。所有可以应用于不需要得到最佳答案,用贪心算法生成近似答案。
1.1简单的作业调度问题
问题:给出n个任务和每个任务的开始和结束时间。找出可以完成的任务的最大数量,在同一时刻只能做一个任务。
vector<int> findCompleteWorkCount(vector<int> start, vector<int> finish)
{
vector<int> result;
int i = 0;
for (int j = 1; j < start.size(); ++j)
{
if (start[j] >= finish[i])
{
result.push_back(j);
i = j;
}
}
return result;
}
1.2最小生成树prim算法
问题:从无向图中寻找一颗最下生成树,一个无向图的最小生成树就是由该图的那些连接G的所有顶点的边构成的树,其总值最低。
方法:算法每一个阶段通过选择边(u,v),使得(u,v)的值是所有u在树上、但v不在树上边的值中最小者。
#define v 5
void printMST(int parent[], int graph[v][v])
{
printf("Edge Weight\n");
for (int i = 1; i < v; i++)
printf("%d - %d %d \n", parent[i], i, graph[i][parent[i]]);
}
void prim(int graph[v][v])
{
int value[v];
bool finish[v];
int parent[v];
for (int i = 1; i < v; ++i)
{
value[i] = graph[0][i];
parent[i] = 0;
finish[i] = false;
}
parent[0] = -1;
finish[0] = true;
for (int i = 1; i < v; ++i)
{
int min_val = INT_MAX, min_index;
for (int j = 1; j<v; ++j)
if (!finish[j] && value[j] != 0 && value[j] < min_val)
{
min_val = value[j];
min_index = j;
}
finish[min_index] = true;
for (int j = 1; j < v; ++j)
if (!finish[j] && graph[min_index][j]!=0&&(value[j] == 0 || graph[min_index][j] < value[j]))
{
parent[j] = min_index;
value[j] = graph[min_index][j];
}
}
printMST(parent, graph);
}
2.分治算法
基本思想:
分:递归解决较小的问题;
治:从子问题的解构建原问题的解
至少包含两个递归调用的例程叫作分治算法
2.1最大字数组问题
问题:输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值
int Start,End,Value;
int FindMaxCrossArray(int a[],int low,int mid,int high)
{
int left_sum=numeric_limits<int>::min();
int right_sum=numeric_limits<int>::min();
int sum=0;
for (int i=mid;i>=low;i--)
{
sum+=a[i];
if(sum>left_sum)
{
left_sum=sum;
Start=i;
}
}
sum=0;
for(int j=mid+1;j<=high;j++)
{
sum+=a[j];
if (sum>right_sum)
{
right_sum=sum;
End=j;
}
}
return left_sum+right_sum;
}
int FindMaxArray(int a[],int low,int high)
{
if (low==high)
{
Start=End=low;
return a[low];
}
int mid=(low+high)/2;
int left_sum=FindMaxArray(a,low,mid);
int right_sum=FindMaxArray(a,mid+1,high);
int cross_sum=FindMaxCrossArray(a,low,mid,high);
if (left_sum>=right_sum&&left_sum>=cross_sum)
{
Start=low;
End=mid;
return left_sum;
}
else if (right_sum>=left_sum&&right_sum>=cross_sum)
{
Start=mid+1;
End=high;
return right_sum;
}
else
return cross_sum;
}
int main()
{
int a[13]={-13,-3,-25,-20,-3,-16,-23,-18,-20,-7,-12,-5,-22};
int result=FindMaxArray(a,0,12);
return 0;
}
2.2最近点对问题
问题:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。严格地说,最接近点对可能多于1对。为了简单起见,这里只限于找其中的一对。
代码待写…
3.动态规划
基本思想:将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
3.1背包问题
问题:给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
int Max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int N,i,j;
int p[100][100];
int C;
vector<Goods> goods;
cout<<"请输入商品总量";
cin>>N;
for (i=0;i<N;i++)
{
cout<<"请输入第"<<i+1<<"个的价值和重量:";
int vi,wi;
cin>>vi>>wi;
goods.push_back(Goods(vi,wi));
}
cout<<"请输入背包最大重量";
cin>>C;
for (i=0;i<N;i++)
p[i][0]=0;
for (j=0;j<C;j++)
p[0][j]=0;
for (i=1;i<N;i++)
for (j=1;j<C;j++)
{
if(j<goods[i].w)p[i][j]=p[i-1][j];
else
p[i][j]=Max(p[i-1][j-goods[i].w]+goods[i].v,p[i-1][j]);
}
return 0;
}
3.2leetcode动态规划题目
- Minimum Path Sum
int minPathSum(vector<vector<int>>& grid) {
if (grid.empty())return 0;
vector<vector<int>> result(grid);
for (int i = 1; i < result[0].size(); ++i)
result[0][i] += result[i][i - 1];
for (int i = 1; i < result.size(); ++i)
result[i][0] += result[i-1][0];
for (int i = 1; i > result.size(); ++i)
for (int j = 1; j < result[i].size(); ++j)
result[i][j] = min(result[i - 1][j] + grid[i][j], result[i][j - 1] + grid[i][j]);
return result[result.size() - 1][result[0].size() - 1];
}
4.回溯算法
基本思想:在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。
一般步骤:
(1)针对所给问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
4.1leetcode相关题目
- Combination Sum
class Solution {
public:
vector<vector<int>> result;
vector<int> data;
int sum(const vector<int>& v)
{
int sum = 0;
for (auto e : v)sum += e;
return sum;
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> temp;
sort(candidates.begin(), candidates.end());
data = candidates;
dfs(temp, target, 0);
return result;
}
void dfs(vector<int> temp, int target, int k)
{
if (k == data.size())return;
if (sum(temp) == target) {
result.push_back(temp); return;}
else if (sum(temp) > target)return;
else
{
for (int i = k; i < data.size(); ++i)
{
temp.push_back(data[i]);
dfs(temp, target, i);
temp.pop_back();
}
}
}
};
2.Subsets
class Solution {
public:
vector<vector<int>> result;
vector<int> temp;
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums, 0, nums.size() - 1);
return result;
}
void dfs(vector<int> nums, int i, int j)
{
result.push_back(temp);
for (int k = i; k <=j; ++k)
{
temp.push_back(nums[k]);
dfs(nums, k + 1, j);
temp.pop_back();
}
}
};
3.Permutations
5.分支界限法
基本思想:类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。但在一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。