- 实验预习报告
一、实验内容
实验内容为动态规划、分支限界、回溯等,包括题目一道简单的Fibonacci、汉诺塔问题等。
二、算法的基本原理
1.动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。动态规划算法的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。主要内容都是与动态规划思想相关,动态规划解题步骤:问题抽象化、建立模型、寻找约束条件、判断是否满足最优性原理、找大问题与小问题的递推关系式、填表、寻找解组成,然后找出01背包问题的最优解 以及解组成,然后编写代码实现。
2.分支限界类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。但在一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。
3.回溯法简单来说就是按照深度优先的顺序,穷举所有可能性的算法,但是回溯算法比暴力穷举法更高明的地方就是回溯算法可以随时判断当前状态是否符合问题的条件。一旦不符合条件,那么就退回到上一个状态,省去了继续往下探索的时间。回溯是递归的副产品,只要有递归就会有回溯。
三、背包问题的理解
背包问题的几种子情况:
经典背包问题:每件物体只能使用一次,在固定容量的情况下,可以放入的最大价值。
完全背包问题:每个物体有无限个只使用i-1个物品的时候所得到的最大值一定小于等于使用i个物品的最大值,而且我们可以枚举当使用0,1,2,3,4,5,。。。,k个第i个物品时的值,找出最大值。
多重背包问题:每个物体有有限个,且知道每个物体的个数。与完全背包问题类似,只是多了一个判断。
分组背包问题:每组物体只能使用其中的一种,这个问题类似于01背包问题每种物体只能选择1次,只是多了一个for循环来遍历选择组合中的哪种物体。
四、预习小结
通过预习本次实验,我的理解是之前学过的通过搜索状态空间树的方法求问题的方法可分为两类:深度优先搜索(DFS)和广度优先搜索(BFS)。如果在运用搜索算法时使用剪枝函数,便成为了回溯法和分支限界法。分支限界法的求解目标是找出满足约束条件的一个解,或是在满足约束条件的解中找出最优解。
本次实验的内容较多,涉及到的算法思想也比较复杂,还好这些思想在之前的实验中有所涉猎,所以整体上减小了做实验的难度,但是本次实验的算法思想必须深刻掌握。
另外在查找资料的时候,我发现了很多使用这种算法思想解决的问题,理解和掌握这些问题能有效地帮助我更好的吃透这种算法思想。
分支限界实验
- 实验目的
1.深入理解背包相关问题。会求解0-1背包问题(贪心算法、动态规划、分支限界法),获得精确最优解或近似最优解均可。能正确设计相应的算法,解决实际问题。
2.通过一个规模较大的实例比较不同方法的求解速度,分析不同算法的时间复杂度,并分析是否能获得最优解。
3.实验结果跟实验设置的参数(如:背包容量、物品的体积)关系很大,简要分析参数对结果的影响。掌握算法时间复杂度分析。
- 可行性分析
动态规划适用的问题的最优解所包含的子问题的解也是最优的,即满足最优化原理;某状态以后的过程不会影响以前的状态,只与当前状态有关;子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。动态规划能够得到全局最优解;也可以得到一族最优解。由于动态规划方法反映了动态过程演变的联系和特征,在计算时可以利用实际知识和经验提高求解效率。但是动态规划没有统一的标准模型。
回溯法和分支限界法的共同点是一种在问题的解空间树上搜索问题解的算法。不同点:求解目标不同,回溯法的目标是找出解空间树满足约束条件的所有解,而分支限界法的求解目标是尽快地找出满足约束条件的一个解;搜索方法不同,回溯法采用深度优先方法搜索解空间,而分支限界法一般采用广度优先或以最小消耗优先的方式搜索解空间树;对扩展结点的扩展方式不同,回溯法中,如果当前的扩展结点不能够再向纵深方向移动,则当前扩展结点就成为死结点,此时应回溯到最近一个活结点处,并使此活结点成为扩展结点。分支限界法中,每一次活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点;存储空间的要求不同,分支限界法的存储空间比回溯法大得多,当内存容量有限时,回溯法成功的可能性更大。
- 方案设计
一、所需知识点
1.动态规划算法通常用于求解某种具有最优性质的问题,在这类问题中,通常会有许多可行解,每一个解都对应一个值,我们希望找到最优值的解。
动态规划法与分治法类似,其基本思想也是将待求解的问题分解为若干个子问题,先求解子问题,在从这些子问题的解得到原问题的解。不同的是,适用于动态规划求解的问题,经分解得到的子问题往往不是相互独立的。动态规划所求问题具有的性质:最优性原理。如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优性原理。无后效性。即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。有重叠子问题。即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)。
- 分支限界法类似于回溯法,是一种采用广度优先算法在问题的解空间树上进行搜索的算法。分支限界法的求解目标是:在问题的解空间上,找到符合约束条件的一个解,在某种情况下也可以称之为最优解。
- 回溯法实际上一个类似穷举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”(即回退),尝试别的路径。回溯法搜索解空间时,通常采用两种策略避免无效搜索,提高回溯的搜索效率:用约束函数 在扩展结点处剪除不满足约束的子树;用限界函数剪去得不到问题解或最优解的子树。
二、数学模型
常见的模型为0-1背包问题
如何把这些物品发入这个有承重质量质量限制的背包中,在不超出背包最大限制的前提下,使得放入背包中的物品总价值最大。
问题描述:已知 l 个物品的质量以及它的价值分别为Wi(i=1,2,3…)和Vi(i=1,2,3…),背包的最大载重量为C。则0-1背包问题可被描述为:选择哪些物品放入背包,使得背包在最大载重量限制之内所装物品的总价值最大?
数学模型如下:
上式中Xi是0-1决策变量,表示物品i是否被装包,如果装包,则Xi= 1,否则,Xi= 0.目标函数(1)表示最大化背包中物品的总价值,约束(2)限制装入背包物品的总质量不大于背包的最大承载量。
三、详细方法
用回溯法解题的一般步骤如下:
1.针对所给问题,确定问题的解空间树,问题的解空间树应至少包含问题的一个(最优)解。
2.确定结点的扩展搜索规则。
3.以深度优先方式搜索解空间树,并在搜索过程中可以采用剪枝函数来避免无效搜索。
- 代码设计与开发
1.一道简单的Fibonacci
测试方案:0 1 2 3 4 5
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int f(int n)
{
if (n == 0) return 7;
if (n == 1) return 11;
int f0 = 7, f1 = 11, f2;
for (int i = 1; i < n; i++)
{
f2 = f0 + f1;
f0 = f1 % 3;
f1 = f2 % 3;
}
return f2;
}
int main()
{
int n;
while (cin >> n) {
if (f(n) % 3 == 0)
printf("Yes\n");
else printf("No\n");
}
return 0;
}
2.换座位
测试方案:
4
2 4 3 1
10
1 3 6 9 0 8 7 5 2 4
代码:
#include <bits/stdc++.h>
using namespace std;
int merge(int arr[], int temp[], int left, int mid, int right)
{
int inv_count = 0;
int i = left;
int j = mid;
int k = left;
while ((i <= mid - 1) && (j <= right))
{
if (arr[i] <= arr[j])
temp[k++] = arr[i++];
else
{
temp[k++] = arr[j++];
inv_count = inv_count + (mid - i);
}
}
while (i <= mid - 1)
temp[k++] = arr[i++];
while (j <= right)
temp[k++] = arr[j++];
for (i=left; i <= right; i++)
arr[i] = temp[i];
return inv_count;
}
int _mergeSort(int arr[], int temp[], int left, int right)
{
int mid, inv_count = 0;
if (right > left)
{
mid = (right + left)/2;
inv_count = _mergeSort(arr, temp, left, mid);
inv_count += _mergeSort(arr, temp, mid+1, right);
inv_count += merge(arr, temp, left, mid+1, right);
}
return inv_count;
}
int countSwaps(int arr[], int n)
{
int temp[n];
return _mergeSort(arr, temp, 0, n - 1);
}
int main(int argv, char** args)
{
int n,*arr;
while((scanf("%d",&n))!=EOF){
arr=(int*)(malloc(n*sizeof(int)));
for(int i=0;i<n;i++){
cin>>arr[i];
}
cout<<countSwaps(arr, n)<<endl;
}
return 0;
}
3.渣渣辉的思考
测试方案:
10
1 3 6 9 0 8 5 7 4 2
代码:#include <cstdio>
#define maxn 5009
int sum[maxn<<2];
void pushUP(int rt)
{
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l, int r, int rt)
{
sum[rt] = 0;
if (l == r) return;
int m = (l+r) >> 1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
}
void update(int p, int l, int r, int rt)
{
if (l == r)
{
++sum[rt];
return;
}
int m = (l+r) >> 1;
if (p <= m) update(p, l, m, rt<<1);
else update(p, m+1, r, rt<<1|1);
pushUP(rt);
}
int query(int L, int R, int l, int r, int rt)
{
if (L <= l && r <= R)
return sum[rt];
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += query(L, R, l, m, rt<<1);
if (R > m) ret += query(L, R, m+1, r, rt<<1|1);
return ret;
}
int x[maxn];
int main()
{
int n, i;
while (scanf("%d", &n) != EOF)
{
build(0, n-1, 1);
int sum = 0;
for (i = 0; i < n; ++i)
{
scanf("%d", x+i);
sum += query(x[i]+1, n-1, 0, n-1, 1);
update(x[i], 0, n-1, 1);
}
int ret = sum;
for (i = 0; i < n; ++i)
{
sum += n - x[i] - 1 - x[i];
ret = ret < sum ? ret : sum;
}
printf("%d\n", ret);
}
return 0;
}
4.汉诺塔问题
测试方案:
2
60 1
3 1
代码:
#include<iostream>
using namespace std;
int main()
{
int t,n,k,i;
long long s;
cin>>t;
while(t--)
{
s=1;
cin>>n>>k;
for(i=0;i<n-k;i++)
{
s*=2;
}
cout<<s<<endl;
}
return 0;
}
5.买股票
测试方案:
3
50 35 40
17
100 113 110 85 105 102 86 63 81 101 94 106 101 79 94 90 97
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100000;
int main()
{
int n;
while (cin >> n)
{
int a[N];
int min = 100000000;
int ans = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (min > a[i])
{
min = a[i];
}
ans = max(ans, a[i] - min);
}
cout << ans<<endl;
}
return 0;
}
6.渣渣辉又重了
测试方案:
5
4 -3 4 -5 3
7
0 3 -1 1 -3 7 -5
代码:
#include<bits/stdc++.h>
using namespace std;
long long a[100001],b[100001],c[100001];
int main(){
long long n;
while(~scanf("%d",&n))
{
long long max=0,d,e;
for(int i=0;i<n;i++){
cin>>a[i];
}
b[0]=a[0];
c[0]=1;
for(int i=1;i<n;i++){
if(b[i-1]>=0){
c[i]=c[i-1];
b[i]=a[i]+b[i-1];
}else{
c[i]=i+1;
b[i]=a[i];
}
if(b[i]>max){
max=b[i];
d=i+1;
e=c[i];
}
}
cout<<max<<" ";
cout<<e<<" "<<d<<endl;
}
return 0;
}
7.Quoit Design
测试方案:
2
0 0
1 1
2
1 1
1 1
3
-1.5 0
0 0
0 1.5
0
代码:
#include "stdio.h"
#include "algorithm"
#include "math.h"
using namespace std;
#define N 100005
struct Point
{
double x, y;
} points[N];
Point id[N];
int sort_x(Point p1, Point p2)
{
return p1.x < p2.x;
}
int sort_y(Point p1, Point p2)
{
return p1.y < p2.y;
}
double dist(Point p1, Point p2)
{
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
double nearest(int l, int r)
{
double d;
if (l == r)
{ return 10e100;
}
else if (l + 1 == r)
{
return dist(points[l], points[r]);
}
int m = (l + r) / 2;
d = min(nearest(l, m), nearest(m + 1, r));
int n = 0, i;
for (i = l; i <= r; i++)
{
if (fabs(points[i].x - points[m].x) <= d)
{
id[n++] = points[i];
}
}
sort(id, id + n, sort_y);
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (id[j].y - id[i].y < d)
{
d = min(dist(id[i], id[j]), d);
}
else
{
break;
}
}
}
return d;
}
int main(int argc, char const *argv[])
{
int n;
double ans;
while (true)
{
scanf("%d", &n);
if (n == 0)
{
break;
}
for (int i = 0; i < n; i++)
{
scanf("%lf %lf", &points[i].x, &points[i].y);
}
sort(points, points + n, sort_x);
ans = nearest(0, n - 1);
printf("%.2lf\n", ans / 2);
}
return 0;
}
- 实验结果展示
- 一道简单的Fibonacci
2.换座位
3.渣渣辉的思考
4.汉诺塔问题
5.买股票
6.渣渣辉又重了
7.Quoit Design
- 终身学习
阅读专业书籍,文献,课堂上认真听讲,观看学习网课。看的书不要局限于课本,要多涉略,通过购买一些专业方向的书或图书馆借阅,经典的专业书籍一定要细细研读。
只有最新的文献才能了解所关注的领域的前沿,能出现在书上的知识大都是陈旧的,这就需要首先了解感兴趣的方向,然后学会文献检索。
网课有慕课(MOOC)、和腾讯课堂,慕课是免费的大学生学习平台,里面授课的教师是各大高校的优秀教师,涉及的内容也非常的丰富,而且每节课的时间都很短,可以合理利用时间碎片,收获知识。
网络上有很多前沿的知识,可以帮助我们更好的理解最先进的算法。尤其是北京大学屈婉玲教授的《算法分析与设计》课程,难度较大,但是内容非常精彩。