算法分析与设计课程实验报告
算法设计与分析课程实验,递归与分治,动态规划,贪心与随机算法
写在开头:
算法真的学的头大,一眼望不到算法的尽头。yls的算法作业也真是暴多,这一学期光算法就写了50+篇报告,期中的时候看别人统计出来的时候真的破防了。但是这么多作业也不算白写,给我打的平时分我还是很满意滴。
声明:本人算法入门水平,佬们没必要浪费时间看这坨代码。
如果有任何违规的地方,请第一时间联系我,我会立即删除本文。
实验一:递归与分治
实验1.3 快速排序及第k小数
1.算法描述:
对于一个已经给出的数组,通过输入一个合法区间[l,r],以及一个数k,先找出[l,r]中第k小的数并输出,再使用快速排序对[l,r]这个区间进行排序,最后输出已经排序好的数值序列。
2.算法思路:
快速排序实际上就是通过一次次地选出一个个参考值,并根据这个参考值,将比他小的排在他左边,比他大的排在他右边,这样进行调整。我们可以先实现调整函数int arrange(int l,int r)以a[l]作为临时参照物,把在[l,r]中比a[l]小的放到它左边,比它大的放在他右边,并最终确定a[l]所在的位置。那么快速排序void quick_sort(int l,int r)就可以通过每一次得知a[l]的位置,将原序列分为三段,除了a[l]这个数不用处理以外,剩下的两段,一个都比他小,另一个都比他大,都要继续调整,即递归调用自己,直到全部因为l>=r而结束后,原序列变为有序序列。而通过arrange 返回的参数到左边界l的距离实际上就是参考数在序列中的序号,通过将这个序号与k进行对比,我们可以得出接下来我们要查找哪边。
3.代码实现:
#include <iostream>
using namespace std;
int a[105]={0,9,2,7,4,5,6,3,8,1};
void swap(int i,int j)
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
int arrange(int l,int r)
{
int temp = a[l];
while(l<r)
{
while(a[r]>=temp&&l<r)
r--;
if(l<r)
swap(l,r);
while(a[l]<=temp&&l<r)
l++;
if(l<r)
swap(l,r);
}
return l;//返回参考点的下标
}
void quick_sort(int l,int r)
{
if(l >= r)
return;
else
{
int mid = arrange(l, r);
quick_sort(l,mid-1);
quick_sort(mid+1,r);
}
}
int quick_sort_mink(int l,int r,int k)
{
int mid = arrange(l, r);
if((mid-l+1) == k)
{
return a[mid];
//若为第k小的,则返回
}
else if ((mid-l+1) > k)
{
return quick_sort_mink(l, mid-1, k);
//若超过第k个,则查找l到mid-1,裁剪[mid,r],因为裁剪的是大的值,所以不改变k
}
else
{
return quick_sort_mink(mid+1, r, k-(mid-l+1));
//若小于k,则将[l,mid]共(mid-l+1)个数全部裁剪,因而从查找原序列的第k个,变为查找新序列的第k-(mid-l+1)个。
}
}
int main()
{
int k,l,r;
cout<<"请输入左边界l的值:";
cin>>l;
cout<<"请输入左边界r的值:";
cin>>r;
if(l>r)
{
cout<<"l不能比r大!!!"<<endl;
return -1;
}
cout<<"请输入k的值:";
cin>>k;
if(r-l+1<k)
{
cout<<"总共就"<<r-l+1<<"个数,你让我找第"<<k<<"小的数?"<<endl;
return -1;
}
cout<<"第"<<k<<"小的数是:"<<quick_sort_mink(l, r, k)<<endl;
quick_sort(l, r);
cout<<"排序后的结果为:";
for(int i=l;i<=r;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
4.算法优化:
快速排序的最坏时间复杂度为O(),比如对于{1,2,3,4}这样的序列进行快速排序,就会出现这样的最坏时间复杂度。对于最坏的情况,T(n) = T(n-1)+T(1)+n,最坏时间复杂度就是由这样的不对等分配导致的。而对于最好的情况,T(n) = 2T(n/2)+n,这样最好时间复杂度就是O().所以如何选取一个合理的参照数字,选到的数字越靠近中间数越好,就是优化快排的关键。
1.从数组的头部,尾部和中间,这三个数里面选择既不是最大的也不是最小的数作为参照数。
2.使用随机数产生参照数的下标,随机数还是很能打的。
实验 1.5 棋盘覆盖问题
1.题干:
在一个×个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的
4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
2.算法描述:
对于一组给出的k,x,y。输出一个除了(x,y)以外,其他方块都不重叠地被骨牌给覆盖了的边长为的正方形棋盘情况图。
3.算法思路:
对于正方形棋盘,我们将其均分成四块,其中1块有特殊方块,另外3块没有特殊方块,为了能使递归进行下去,我们必须在另外3块中创造出特殊方块(或者说已经被占领的方块)。
创造情况如下图:
1.特殊点在左上角:
红色为新的特殊点;
2.特殊点在右上角:
蓝色为新的特殊点;
3.特殊点在左下角:
橙黄色的为新的特殊点。
4.特殊点在右下角,则左上角区块的最右下角的方块,右上角区块的最左下角的方块,左下角区块的最右上角的方块,作为新的特殊点。
最后,若区块的尺寸缩小到单位长度1时,结束递归。
4.代码实现:
#include <iostream>
using namespace std;
int map[105][105]={0};
void draw(int x,int y,int lx,int ly,int l)//(x,y)为特殊点的坐标,(lx,ly)为正方形棋盘的左上角的坐标,l为棋盘的边长
{
if(l == 1)
return ;
int halfx = lx+l/2-1,halfy = ly+l/2-1;
if(x<=halfx)
{
if(y<=halfy)//若特殊点在左上角,则在最中间的2x2个方格中放入一个d类骨牌,并将放入的骨牌当作新的特殊点
{
map[halfx][halfy+1] = 4;//4代表此方块被d类骨牌占领
map[halfx+1][halfy] = 4;
map[halfx+1][halfy+1] = 4;
draw(x, y, lx, ly, l/2);
draw(halfx,halfy+1,lx,halfy+1,l/2);
draw(halfx+1,halfy,halfx+1,ly,l/2);
draw(halfx+1,halfy+1,halfx+1,halfy+1,l/2);
}
else//若特殊点在右上角,则在最中间的2x2个方格中放入一个c类骨牌,并将放入的骨牌当作新的特殊点
{
map[halfx][halfy] = 3;//3代表此方块被c类骨牌占领
map[halfx+1][halfy] = 3;
map[halfx+1][halfy+1] = 3;
draw(halfx, halfy, lx, ly, l/2);
draw(x,y,lx,halfy+1,l/2);
draw(halfx+1,halfy,halfx+1,ly,l/2);
draw(halfx+1,halfy+1,halfx+1,halfy+1,l/2);
}
}
else
{
if(y<=halfy)//若特殊点在左下角,则在最中间的2x2个方格中放入一个b类骨牌,并将放入的骨牌当作新的特殊点
{
map[halfx][halfy] = 2;//2代表此方块被b类骨牌占领
map[halfx][halfy+1] = 2;
map[halfx+1][halfy+1] = 2;
draw(halfx, halfy, lx, ly, l/2);
draw(halfx,halfy+1,lx,halfy+1,l/2);
draw(x,y,halfx+1,ly,l/2);
draw(halfx+1,halfy+1,halfx+1,halfy+1,l/2);
}
else//若特殊点在右下角,则在最中间的2x2个方格中放入一个a类骨牌,并将放入的骨牌当作新的特殊点
{
map[halfx][halfy] = 1;//1代表此方块被a类骨牌占领
map[halfx][halfy+1] = 1;
map[halfx+1][halfy] = 1;
draw(halfx, halfy, lx, ly, l/2);
draw(halfx,halfy+1,lx,halfy+1,l/2);
draw(halfx+1,halfy,halfx+1,ly,l/2);
draw(x,y,halfx+1,halfy+1,l/2);
}
}
}
int quickPower(int a, int b)//是求a的b次方
{
int ans = 1, base = a;//ans为答案,base为a^(2^n)
while(b > 0)//b是一个变化的二进制数,如果还没有用完
{
if(b & 1)//&是位运算,b&1表示b在二进制下最后一位是不是1,如果是:
ans *= base;//把ans乘上对应的a^(2^n)
base *= base;//base自乘,由a^(2^n)变成a^(2^(n+1))
b >>= 1;//位运算,b右移一位,如101变成10(把最右边的1移掉了),10010变成1001。现在b在二进制下最后一位是刚刚的倒数第二位。结合上面b & 1食用更佳
}
return ans;
}
int main()
{
int k,x,y,l;
cout<<"请输入k的值(1~6):";
cin>>k;
if(k<1||k>6)
{
cout<<"k的取值非法,请遵守提示!!!"<<endl;
return -1;
}
cout<<"请输入特殊方格的x坐标(1<=x<=2^k):";
cin>>x;
cout<<"请输入特殊方格的y坐标(1<=y<=2^k):";
cin>>y;
if(x<1||y<1)
{
cout<<"横、纵坐标必须大于0哦。"<<endl;
return -1;
}
l = quickPower(2, k);
if(x>l||y>l)
{
cout<<"边长才为"<<l<<",你叫我上哪里去找("<<x<<","<<y<<")?"<<endl;
return -1;
}
draw(x, y, 1, 1, l);
for(int i=1;i<=l;i++)
{
for(int j=1;j<=l;j++)
{
cout<<map[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
5.算法优化:
我没想到有哪里可以优化的。。。
实验四:动态规划
实验4.2 计算矩阵连乘积
1.题干:
在科学计算中经常要计算矩阵的乘积。矩阵A和B可乘的条件是矩阵A的列数等于矩阵B的行数。若A是一个p×q的矩阵,B是一个q×r的矩阵,则其乘积C=AB是一个p×r的矩阵。由该公式知计算C=AB总共需要pqr次的数乘。其标准计算公式为:
现在的问题是,给定n个矩阵{A1,A2,…,An}。其中Ai与Ai+1是可乘的,i=1,2,…,n-1。要求计算出这n个矩阵的连乘积A1A2…An的最少数乘次数。
2.算法描述:
对于输入的矩阵个数n,以及各个矩阵的行数,列数。得到一个最小的乘积次数以及对应的方案。
3.算法思路:
采用动态规划,写出状态转移方程:
m[i][j]表示从第i个矩阵乘到第j个矩阵所需的最少乘法次数。
随后进行动态规划,注意因为m[i][j]的计算需要m[i][k]和m[k+1][j]所以必须先将i,j相差小的给计算出来,才能继续算i,j相差大的。
4.代码实现:
#include <iostream>
using namespace std;
int s[105][105];
void showhow(int l,int r)//展示分割矩阵
{
if(l>=r)
return;
else
{
cout<<l<<"号矩阵到"<<r<<"号矩阵的分割矩阵为:"<<s[l][r]<<endl;
showhow(l,s[l][r]);
showhow(s[l][r]+1,r);
}
}
int main()
{
int n,p[105],i,j,r,k,cnt[105][105],tempcnt;
cout<<"请输入矩阵的个数(1<n<101):";
cin>>n;
if(n<2||n>100)
{
cout<<"矩阵个数非法!!!"<<endl;
return -1;
}
cout<<"请输入第1个矩阵的行数:";
cin>>p[0];
if(p[0]<1)
{
cout<<"行数非法!!!"<<endl;
return -1;
}
for(i = 1;i<n;i++)
{
cout<<"请输入第"<<i<<"个矩阵的列数(同时也是第"<<i+1<<"个矩阵的行数):";
cin>>p[i];
if(p[i]<1)
{
cout<<"列数非法!!!"<<endl;
return -1;
}
}
cout<<"请输入第"<<i<<"个矩阵的列数:";
cin>>p[n];
if(p[n]<1)
{
cout<<"列数非法!!!"<<endl;
return -1;
}
for(i = 1;i<=n;i++)
{
cnt[i][i]=0;
s[i][i]=0;
}
i = 1;
for(r=1;i+r<=n;r++)//i与j相差多少,并保证j不超过n
{
for(;i<n;i++)
{
j = i+r;
cnt[i][j] = cnt[i+1][j]+p[i-1]*p[i]*p[j];//初始化分法
s[i][j] = i;
for(k=i+1;k<j;k++)
{
tempcnt = cnt[i][k]+cnt[k+1][j]+p[i-1]*p[k]*p[j];//查找是否有更好的分法
if(tempcnt<cnt[i][j])
{
cnt[i][j] = tempcnt;
s[i][j] = k;
}
}
}
i=1;
}
cout<<"最少乘积次数为:"<<cnt[1][n]<<endl;
showhow(1, n);
return 0;
}
5.算法优化:
没什么想法。。。
实验4.4 防卫导弹
1.题干:
一种新型的防卫导弹可截击多个攻击导弹。它可以向前飞行,也可以用很快的速度向下飞行,可以毫无损伤地截击进攻导弹,但不可以向后或向上飞行。但有一个缺点,尽管它发射时可以达到任意高度,但它只能截击比它上次截击导弹时所处高度低或者高度相同的导弹。现对这种新型防卫导弹进行测试,在每一次测试中,发射一系列的测试导弹(这些导弹发射的间隔时间固定,飞行速度相同),该防卫导弹所能获得的信息包括各进攻导弹的高度,以及它们发射次序。现要求编一程序,求在每次测试中,该防卫导弹最多能截击的进攻导弹数量,一个导弹能被截击应满足下列两个条件之一:
a)它是该次测试中第一个被防卫导弹截击的导弹;
b)它是在上一次被截击导弹的发射后发射,且高度不大于上一次被截击导弹的高度的导弹。
2.算法描述:
对于输入的导弹个数n,以及一系列导弹的高度h[n],输出能拦截的最大导弹个数。
3.算法思路:
本题为动态规划题,初始化dp[i-1] = 1。写出动态方程:若有比i-1号导弹更低的导弹,则dp[i-1] = max(dp[i-1],dp[j]+1)。
dp[i]表示在拦截i号导弹的前提下,所能拦截的最大导弹个数,最终在所有情况中找出拦截导弹个数最多的情况。
4.代码实现:
#include <iostream>
using namespace std;
int maxcnt(int a[],int l,int r)
{
if(l==r)
{
return a[l];
}
else
{
int mid = (l+r)/2;
return max(maxcnt(a,l,mid),maxcnt(a,mid+1,r));
}
}
int max(int a,int b)
{
return a>=b?a:b;
}
int main()
{
int n,h[105],dp[105],i,j;
cout<<"请输入导弹的个数(小于101个):";
cin>>n;
if(n<1||n>100)
{
cout<<"非法导弹个数!!!"<<endl;
return -1;
}
for(i=1;i<=n;i++)
{
cout<<"请输入第"<<i<<"个导弹的高度:";
cin>>h[i];
dp[i] = 1;
if(h[i]<1)
{
cout<<"非法导弹高度!!!"<<endl;
return -1;
}
}
for(i= n;i>1;i--)
{
for(j=i;j<=n;j++)
{
if(h[i-1]>=h[j])//若第i-1个导弹的高度大于等于第j个的导弹的高度,则取两者能拦截的大者
{
dp[i-1] = max(dp[i-1],dp[j]+1);
}
}
}
cout<<"最多能拦截"<<maxcnt(dp, 1, n)<<"个导弹"<<endl;
return 0;
}
5.算法优化:
没啥想法。。。
实验4.9 皇宫看守
1.题干:
太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
请你编程计算帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
2.算法描述:
对于输入的节点个数n,以及各个结点的相关信息:此节点要花费的经费,子节点的个数,以及各个子节点的序号(保存在trees数组中),输出一个最小费用。
3.算法思路:
本实验依旧采用动态规划的方法。用dp[i]表示看守以i为根的皇宫所需要的最小经费。
对于这种只有根结点的树,他的dp = cost。
对于其他的树,则需要比较:1.根节点的花费+子代的子代的dp花费;2.子代的dp花费。最后取二者小的那一方dp[i] = min(trees[i].cost+sum,childcost(trees, i))
4.代码实现:
#include <iostream>
using namespace std;
class tree
{
public:
int cost;
int childnum;
int childid[105];
tree(int c,int cn)
{
this->cost = c;
this->childnum = cn;
for(int i=1;i<=cn;i++)
{
cin>>childid[i];
}
}
tree(){}
};
int childcost(tree a[],int id)//求出编号为id的所有孩子的最小经费之和
{
int i,sum=0;
for(i=1;i<=a[id].childnum;i++)
{
sum+=a[a[id].childid[i]].cost;
}
return sum;
}
int max(int a,int b)
{
return a>b?a:b;
}
int maxone(int a[],int l,int r)//二分法求数组最大值
{
if(l == r)
return a[l];
else
{
int mid = (l+r)/2;
return max(maxone(a,l,mid),maxone(a, mid+1, r));
}
}
int min(int a,int b)
{
return a<=b?a:b;
}
int main()
{
int n,i,tempc,tempcn,num,dp[105];//dp[i]表示看守以i为根的树所需要的最小经费
tree trees[105];
cout<<"请输入结点的个数(小于101):";
cin>>n;
for(i=1;i<=n;i++)
{
cout<<"请输入"<<i<<"号结点的相关信息:";
cin>>tempc>>tempcn;
trees[i] = tree(tempc,tempcn);
}
for(num=0;num<n-1;num++)
{
for(i = 1;i<=n;i++)
{
if(trees[i].childnum == num)
{
if(num == 0)
{
dp[i] = trees[i].cost;
}
else
{
int sum = 0;
for(int j = 1;j<=trees[i].childnum;j++)
{
sum+= childcost(trees, trees[i].childid[j]);
}
dp[i] = min(trees[i].cost+sum,childcost(trees, i));//要么自己的耗费+孩子的孩子的耗费之和,要么孩子的耗费之和,二者取小
}
}
}
}
cout<<maxone(dp,1,n)<<endl;//在dp数组中开销最大的肯定是根结点。
return 0;
}
5.算法优化:
我不到怎么优化啊。。。
实验五:贪心算法和随机算法
实验5.1 背包问题
1.题干:
有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品
A
B
C
D
E
F
G
重量
35
30
60
50
40
10
25
价值
10
40
30
50
35
40
30
2.算法描述:
对于给出的背包容量m,物品个数n,以及各个物品的重量w和总价值v,输出背包能承载的最大总价值,以及放入物品的种类和多少。
3.算法思路:
采用贪心算法,对所有物品按照单位重量的价值进行排序,优先选单位重量的价值高者的全部,最后再取剩下的单位重量的价值高者的部分。
4.代码实现:
#include <iostream>
using namespace std;
void swap(int a[],int i1,int i2)
{
int temp = a[i1];
a[i1] = a[i2];
a[i2] = temp;
return;
}
void swap(double a[],int i1,int i2)
{
double temp = a[i1];
a[i1] = a[i2];
a[i2] = temp;
return;
}
int main()
{
int n,index[105],w[105],v[105],m,i,j;
double sum = 0,v_per_w[105];
cout<<"请输入背包的容量:";
cin>>m;
cout<<"请输入物品的个数:";
cin>>n;
for(i = 1;i<=n;i++)
{
index[i] = i;
cout<<"请输入"<<i<<"号物品的重量:";
cin>>w[i];
cout<<"请输入"<<i<<"号物品的总价值:";
cin>>v[i];
v_per_w[i] = (1.0*v[i])/w[i];
}
for(i=1;i<n;i++)
{
int max = i;
for(j=i+1;j<=n;j++)
{
if(v_per_w[j]>v_per_w[max])
max = j;
}
if(max != i)
{
swap(index,i,max);
swap(w,i,max);
swap(v,i,max);
swap(v_per_w,i,max);
}
cout<<v_per_w[i]<<endl;
}
i=1;
while(m>w[i])
{
sum+=v[i];
m-=w[i];
i++;
}
sum+= (v_per_w[i]*m);
double percent = (m*1.0)/w[i];
cout<<"总价值最大为:"<<sum<<endl;
cout<<"方案为:"<<endl;
for(int j=1;j<i;j++)
{
cout<<index[j]<<"号物品取全部"<<endl;
}
cout<<index[i]<<"号物品取"<<percent<<endl;
return 0;
}
5.算法优化:
这玩意真没有优化的必要了吧。。。
实验5.2 照亮的山景
1.题干:
在一片山的上空,高度为T处有N个处于不同位置的灯泡,如图。如果山的边界上某一点于某灯i的连线不经过山的其它点,我们称灯i可以照亮该点。开尽量少的灯,使得整个山景都被照亮。山被表示成有m个转折点的折线。
提示:照亮整个山景相当于照亮每一个转折点。
2.算法描述:
对于输入的灯泡高度t,灯泡个数n,每个灯泡的横坐标l_x,折点个数m,每个折点的坐标(xi,yi),输出最少的开灯组合。
3.算法思路:
通过计算灯泡到折点的斜率来确定哪些点能被这个灯泡照到:对于x大于灯泡的横坐标l_x的点,后面能照到的折点和灯泡构成的直线的斜率为非减序列;而对于x小于灯泡横坐标l_x的点,前面能照到的折点和灯泡构成的直线的斜率为非递增序列。
1.
2.
3.
4.
5.
6.
7.
确定好每个灯能照到哪些点后,这个问题就变成了,如何取子集,使这些子集的交集为全集,且子集个数最少。这好像是一个NPC问题,目前没有多项式复杂度的解法,所以我采用模拟退火算法这种启发式算法来求解。效果也还可以。
4.代码实现:
#include <iostream>
using namespace std;
int cnt[105],details[105][105],a[105]={0};//cnt[k]表示k号灯泡能照到的折点的个数,details[k][]是具体哪个折点能被k号灯泡照到
double check(int get[],int n,int m)//对于一种亮灯泡的方案get[],计算这种方案能照亮的折点的百分比
{
int i,j,count=0;
for(i=1;i<=m;i++)
{
a[i] = 0;
}
for(i=1;i<=n;i++)
{
if(get[i] == 1)
{
for(j=1;j<=cnt[i];j++)
{
a[details[i][j]] = 1;
}
}
}
for(i=1;i<=m;i++)
{
if(a[i] == 1)
{
count++;
}
}
double percent = (double)((1.0*count)/m);
return percent;
}
int main()
{
int t,n,m,l_x[105],x[105],y[105],l,r,i;
cout<<"请输入灯泡的高度T以及灯泡的个数N:"<<endl;
cin>>t>>n;
cout<<"请输入"<<n<<"个灯泡的横坐标:"<<endl;
for(i=1;i<=n;i++)
{
cin>>l_x[i];
}
cout<<"请输入折点的个数:"<<endl;
cin>>m;
cout<<"请输入"<<m<<"个折点的坐标(请按横坐标依次增大的顺序输入):"<<endl;
for(i=1;i<=m;i++)
{
cin>>x[i]>>y[i];
}
for(int k=1;k<=n;k++)
{
r=1;
while(l_x[k]>x[r])//找到第一个横坐标大于等于这个灯泡的点
r++;
if(l_x[k] == x[r])//如果这个点正好在灯泡下方,此时斜率为∞,分母为0会报错,所以要特殊处理
{
cnt[k]++;
details[k][cnt[k]] = r;//记录这个点
l = r-1;//设置左边的起点
r = r+1;//设置右边的起点
}
else//这个点不在灯泡正下方
{
l = r-1;//设置左边的起点
r = r;//设置右边的起点
}
if(r<=m)//如果右边还有点
{
double k_board = (1.0*(t-y[r]))/(l_x[k]-x[r]);//计算标记点和灯泡组成的直线的斜率
cnt[k]++;
details[k][cnt[k]] = r;
for(i=r+1;i<=m;i++)
{
double temp_k = (1.0*(t-y[i]))/(l_x[k]-x[i]);
if(temp_k>=k_board)//若后面的斜率变大了,那么就更新屏障的斜率
{
cnt[k]++;
details[k][cnt[k]] = i;
k_board = temp_k;
}
}
}
if(l>0)//如果左边还有点
{
double k_board = (1.0*(t-y[l]))/(l_x[k]-x[l]);
cnt[k]++;
details[k][cnt[k]] = l;
for(i=l-1;i>0;i--)
{
double temp_k = (1.0*(t-y[i]))/(l_x[k]-x[i]);
if(temp_k<=k_board)//若前面的斜率变小了,那么就更新屏障的斜率
{
cnt[k]++;
details[k][cnt[k]] = i;
k_board = temp_k;
}
}
}
}
//模拟退火部分
int bestget[105]={0},tempget[105]={0},nowget[105]={0},bestsize=0,tempsize=0,nowsize=0;
//best/temp/nowget分别为最佳,暂时,目前的亮灯方案。best/temp/nowsize分别为最佳,暂时,目前的亮灯个数
double temperature = 200,a = 0.99,bestmax=0,tempmax=0,nowmax=0;//best/temp/nowget分别为最佳,暂时,目前的照到的折点的百分比。
while(temperature > 0.1)
{
for(i=1;i<=100;i++)
{
int random_index = (int)(rand()%n)+1;
nowget[random_index] = (nowget[random_index]+1)%2;
nowmax = check(nowget, n, m);
nowsize = 0;
for(int j=1;j<=m;j++)
{
if(nowget[j])
nowsize++;
}
if(nowmax>=tempmax||(tempmax - nowmax)/temperature>(1.0*rand())/RAND_MAX)
{
if((nowmax == tempmax) && (nowsize > tempsize))
continue;
tempmax = nowmax;
tempsize = nowsize;
for(int j=1;j<=n;j++)
{
tempget[j] = nowget[j];
}
}
}
if(tempmax>=bestmax)
{
if((tempmax == bestmax) && (tempsize > bestsize))
continue;
bestmax = tempmax;
bestsize = tempsize;
for(int j=1;j<=n;j++)
{
bestget[j] = tempget[j];
}
}
temperature*=a;
}
for(i=1;i<=n;i++)
{
cout<<bestget[i]<<" ";
}
cout<<endl;
return 0;
}
5.算法优化:
模拟退火部分的参数应该可以调整优化,但是我不是很懂参数设置。
实验5.3 搬桌子问题
1.题干:
某教学大楼一层有n个教室,从左到右依次编号为1、2、…、n。现在要把一些课桌从某些教室搬到另外一些教室,每张桌子都是从编号较小的教室搬到编号较大的教室,每一趟,都是从左到右走,搬完一张课桌后,可以继续从当前位置或往右走搬另一张桌子。输入数据:先输入n、m,然后紧接着m行输入这m张要搬课桌的起始教室和目标教室。输出数据:最少需要跑几趟。
2.算法描述:
对于输入的教室个数n,桌子个数m,以及各个桌子的起始教室start和目标教室end,给出全部搬完的最少的趟数。
3.算法思路:
采用贪心算法,优先搬移起始教室最近的桌子(或者目标教室最近的教室好像也可以?)即可。
4.代码实现:
#include <iostream>
using namespace std;
void swap(int a[],int i1,int i2)
{
int temp = a[i1];
a[i1] = a[i2];
a[i2] = temp;
return ;
}
int main()
{
int n,m,start[105],end[105],used[105]={0},i,j,cnt=0,num=0,temp;
cout<<"请输入教室的个数n和桌子的个数m:";
cin>>n>>m;
for(i=1;i<=m;i++)
{
cout<<"请输入第"<<i<<"个桌子的起始教室和目标教室:";
cin>>start[i]>>end[i];
}
for(i=1;i<m;i++)
{
int min = i;
for(j=i+1;j<=m;j++)
{
if(start[j]<start[min])
min = j;
}
if(min != j)
{
swap(start,i,min);
swap(end,i,min);
}
}
while(num<m)
{
temp = 0;
for(i=1;i<=n;i++)
{
if(used[i] == 0&&start[i]>=temp)
{
temp = end[i];
used[i] = 1;
num++;
}
}
cnt++;
}
cout<<cnt<<endl;
return 0;
}
5.算法优化:
贪心好像也没什么好优化的。。。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)