疯鱼尾的短学期第一天

二分算法学习总结,心得体会

罗勇军老师的第一遍博客写的是关于二分法和三分法,主要内容是二分法
链接: 算法竞赛专题解析(1):二分法、三分法.

1.二分法适用的题目类型

二分非常高效。所以,如果问题是单调性的,且求解精确解的难度很高,可以考虑用二分法。(这是原话)
总结一下有两点:1.问题是单调的,有时候需要手动排序。2.求解的精度很高,在实数二分的时候,就要控制二分的精度了。

2.整数二分问题

2.1解题模板

while (left < right) {
        int ans;          //记录答案
        int mid = left+(right-left)/2;   //二分
        if (check(mid)){  //检查条件,如果成立
            ans = mid;    //记录答案//移动left(或right)
        }
        else//移动right(或left)
}

check函数非常重要,它的作用是检查mid是否符合条件。那么问题就来了,如何检查。这个地方出题者可以大做文章,可以跟很多其他的知识进行结合考察。

2.2主要问题类型

  1. 最小化最大值问题
  2. 最大化最小值问题
    下面这段代码是我昨天晚上尝试这写“通往奥格瑞玛的道路”这个题的一个半成品的代码。放一个没写出来的代码在这里,主要是感觉这个题综合性太大,很多知识还没学好,写出来很困难。

这是一份尚未完成的代码,昨天自闭了一晚上,尽管已经看懂了算法的步骤,还是感觉很多知识没学好,写不出来。

#include<bits/stdc++.h>
using namespace std;

// 邻接矩阵
typedef struct _graph
{
    int vexs[MAX];       // 顶点集合
    int vexnum;           // 顶点数
    int edgnum;           // 边数
    int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;

// 边的结构体
typedef struct _EdgeData
{
    char start; // 边的起点
    char end;   // 边的终点
    int weight; // 边的权重
}EData;

int dijkstra(Graph G, int vs, int prev[], int dist[]){
	 int i,j,k;
    int min;
    int tmp;
    int flag[MAX];      // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。

    // 初始化
    for (i = 0; i < G.vexnum; i++)
    {
        flag[i] = 0;              // 顶点i的最短路径还没获取到。
        prev[i] = 0;              // 顶点i的前驱顶点为0。
        dist[i] = G.matrix[vs][i];// 顶点i的最短路径为"顶点vs"到"顶点i"的权。
    }

    // 对"顶点vs"自身进行初始化
    flag[vs] = 1;
    dist[vs] = 0;

    // 遍历G.vexnum-1次;每次找出一个顶点的最短路径。
    for (i = 1; i < G.vexnum; i++)
    {
        // 寻找当前最小的路径;
        // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
        min = INF;
        for (j = 0; j < G.vexnum; j++)
        {
            if (flag[j]==0 && dist[j]<min)
            {
                min = dist[j];
                k = j;
            }
        }
        // 标记"顶点k"为已经获取到最短路径
        flag[k] = 1;

        // 修正当前最短路径和前驱顶点
        // 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
        for (j = 0; j < G.vexnum; j++)
        {
            tmp = (G.matrix[k][j]==INF ? INF : (min + G.matrix[k][j])); // 防止溢出
            if (flag[j] == 0 && (tmp  < dist[j]) )
            {
                dist[j] = tmp;
                prev[j] = k;
            }
        }
    }

	printf("dijkstra(%c): \n", G.vexs[vs]);
    	for (i = 0; i < G.vexnum; i++)
        	printf("  shortest(%c, %c)=%d\n", G.vexs[vs], G.vexs[i], dist[i]);

}


int main(){
	int n,m,b;
	cin>>n>>m>>b;
	int fi[n];//经过城市i,需要交费fi元
	for(int i=0;i<n;i++){
		cin>>fi[i];
	}
	int ai[m];
	int bi[m];
	int ci[m];//ai城到bi城需要ci的血量 
	for(int i=0;i<m;i++){
		cin>>ai[i]>>bi[i]>>ci[i];
	}
	
	//二分
	
	sort(fi,fi+n-1) ;//对fi进行排序
	int left=0;int right=n;
	while(left<right){
		int mid=left+(right-left)/2;
		
		//
		//将当前条件转化为一张图 
		Graph G;//图 
		G.vexnum=n;
		//写不出来了,脑阔疼。。。
		
		///
		if(b>=dijkstra(,)){//求最短路径问题 dijkstra算法,删除所有大于fi的点,在剩下的点中,求1到N的最短路。 
			right=mid;//满足条件去找更小的fi 
		}
		else{
			left=mid;//不满足条件则fi需要增大 
		}
	}
	return fi[left]; 
	
	
}      

3.实数二分问题

3.1解题模板

const double eps =1e-7;        //精度。如果下面用for,可以不要eps
while(right - left > eps){     //for(int i = 0; i<100; i++){
      double mid = left+(right-left)/2;
      if (check(mid)) right = mid;           //判定,然后继续二分
      else            left  = mid;
}

两种不同的循环方法都是为了控制了精度。

3.2主要问题类型

其实跟整数二分差不多,只是在check函数上要根据题目做相应的变化。一定要读懂题目。

4.三分法求极值

三分法用来求一个单峰(或者单谷)的极值,是二分法的一个简单扩展。具体内容我就不再这赘述了。跟二分法非常类似。

5.其他知识积累

  • c++的STL中有lower_bound()和upper_bound()两个函数,他们的主要作用是,在不改变数组有序性的前提下,插入一个值,找到这个值的插入位置。区别:lower_bound()是从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。upper_bound()是从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。一个等于的区别。但是两个函数联合起来就真的是1+1>2了。
  • 1.查找第一个大于x的元素的位置:upper_bound()。代码例如:
            pos = upper_bound(a, a+n, test) - a;
    2.查找第一个等于或者大于x的元素:lower_bound()。
    3.查找第一个与x相等的元素:lower_bound()且 = x。
    4.查找最后一个与x相等的元素:upper_bound()的前一个且 = x。
    5.查找最后一个等于或者小于x的元素:upper_bound()的前一个。
    6.查找最后一个小于x的元素:lower_bound()的前一个。
    7.单调序列中数x的个数:upper_bound() - lower_bound()。
  • 在计算mid时的一个小细节我感觉需要注意一下:最好是用left+(right-left)/2而不是直接(left+right)/2。我之前经常用后一种,也没感觉有什么问题。但是当right和left都很大时,就很有可能发生越界导致程序出错。

6.明日更新内容

明天将会更新二分法和三分法的习题,可能会需要两天时间。但是我会尽快更新的!!!能一天绝不两天。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值