Program design final exam review topic two: two points

The second topic is the dichotomy algorithm.

Basic usage :

The dichotomy algorithm is to use each half of the ordered array to make a judgment to find a specific element (the element may be directly the data given, or it may be the answer required by the binary search).

Time complexity: O (logn).

For a common one-dimensional array, the basic algorithm for searching for a specified element :

int binarySearch(int a[],int n,int k)
{
	int l=0,r=n-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(a[mid]==k)
		    return mid;
		else if(a[mid]>k)
		    r=mid-1;
		else l=mid+1;
	}
	return -1;
}

The above is the most common way of writing. If there are consecutive elements to be found in the array, the above algorithm will not care about the position of these consecutive elements. As long as the element is found, the subscript of that element will be returned. For example: Find the element 5, from 5 5 5 5 6 7 8 9 10 11, so the returned index is 2.

The following two simple ways of writing can return the smallest or largest subscript that matches a continuous element.

Returns the minimum coordinates of the continuous elements to be found :

Here, pay attention to the subscript found in the middle of the ans record, and if you use mid = (l + r + 1) / 2, r = mid, or do not use 0 subscript at the same time; or other ways to avoid various endless loops are also No way. Try more examples (for example: 5 5 5 6 7 find 5; 1 2 3 5 5 find 5; etc.). Therefore, it is better to directly use the index found in the middle of the ans record, and no longer consider mid when changing r and l, so that the endless loop can be completely avoided. (This method is also used when seeking the maximum coordinates that match the result)

int binarySearch1(int a[],int n,int k)
{
	int l=0,r=n-1,ans=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(a[mid]>=k)
		    r=mid-1,ans=mid;
		else l=mid+1;
	}
	return ans;
}

Returns the maximum coordinate of the continuous element to find :

int binarySearch2(int a[],int n,int k)
{
	int l=0,r=n-1,ans=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(a[mid]<=k)
		    l=mid+1,ans=mid;
		else r=mid-1;
	}
	return ans;
}

Previously, my understanding of the dichotomy algorithm was limited to the most common search algorithm, but the dichotomy algorithm is used far more than searching for specific elements in a one-dimensional array.

The above is the dichotomous simple idea and application.

The general idea of dichotomy is to decompose a problem of size n into k (usually 2) sub-problems of smaller size. These sub-problems are independent of each other and have the same nature as the original problem. Find the solution to the sub-problem, and then get the solution to the original problem through the sub-problem. It belongs to the branch algorithm.

Extended usage :

1. Find the root of the equation .

Generally this kind of problem is given an equation, for example: x ^ 3 + x ^ 2 + x + 23 = 0. Find a solution to this equation (or specify a range), and specify the exact number of digits after the decimal point.

Example: x ^ 6 + x-2014 = 0 in

For a solution from 0 to positive infinity, 5 decimal places are reserved after the decimal point.

Solution: First of all, we must judge the monotonicity of the function expression in this range, because we need to use the binary search method to find the results. If it is judged to be a monotonically increasing function, then the judgment function should be set when the binary search result is found, f (mid) * f (r) <= 0: the solution is between mid ~ r; otherwise it is between l ~ mid between. In this way, the left and right boundaries can be modified. In addition, when doing this kind of problem, it is best to judge for yourself and set a general scope. For example [1,10]

Code:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double EPS=1e-8;

double f(double x)
{
	return x*x*x*x*x*x + x - 2014;
}

int main()
{
	double l=1,r=10,mid;
	while(abs(r-l)>EPS)
	{
		mid=(l+r)/2;
		if(f(r)*f(mid)<=0)
		    l=mid;
		else r=mid;
	}
	printf("%.5f\n",l);
	return 0;
}

The accuracy of the above question is accurate to 8 decimal places, and then only 5 digits are output. The result obtained is 3.55262. Actually calculate and find that the results differ by 10 to the order of the 7th power. Prove that the results are correct.

2. Binary answer (minimum maximum or minimum maximum) .

(1) There are n cables, and their lengths are len [i]. If k cables of the same length are cut from the n cables, what is the longest length of the k cables?

For example: n: 5; k: 5; len [i] are: 2 5 8 3 4, then the cut length is: 3. Another example: n: 4; k: 11; len [i] are: 8 7 4 5, then the cut length is: 2.

Solution: At first glance, this question is not a model that can be used by conventional dichotomy algorithms. But consider this: we require a maximum value that can be divided into so many segments. If it is less than this value, it must be divided into so many segments. If it is greater than this value, it must not be divided into so many segments. This is like the judgment condition when we use the dichotomy algorithm to change the boundary value. So if we use the idea of ​​dichotomy, for the result we require, first set a range, and then divide the range by two, judge whether the intermediate value meets the conditions of the split, and then constantly change the boundary value to complete the dichotomy. There are two questions here: 1. The judgment condition for changing the boundary value; 2. When to stop changing to get the correct answer.

After these two problems are solved, you can write the code in a dichotomous format:

#include<iostream>
using namespace std;
int k,n;
int len[10005];

bool can(int x)
{
	int num=0;
	for(int i=0;i<n;i++)
	{
		num+=len[i]/x;
	}
	return num>=k;
}

int find(int left,int right)
{
	int l=left,r=right,ans=0;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(can(mid))
		{
		    l=mid+1;ans=mid;	
		}
		else r=mid-1;
	}
	return ans;
}

int main()
{
	cin>>n>>k;
	int max=0;
	for(int i=0;i<n;i++)
	{
		cin>>len[i];
		max=max>len[i]?max:len[i];
	}
	int ans=find(0,max);
	cout<<ans<<endl;
	return 0;
}

Can function can be divided into so many. The find function indicates that l> r stops searching, so the complexity of this program is also O (logn).

(2)Aggressive cows(poj2456)

Topic: There are n bullpens at the position of x [i], and then m cows, keeping them as far apart as possible.

Solution: The dichotomy distance, to determine whether each cow meets this distance, take the maximum dichotomy result.

AC code:

#include<iostream>
#include<algorithm>
using namespace std;

int n,m;
int x[100005];

bool can(int dist)
{
	int num=1;
	int cur=0;
	for(int i=0;i<n;i++)
	{
		if(x[i]-x[cur]>=dist)
		{
			cur=i;
			num++;
		}
	}
	return num>=m;
}

int maxdist()
{
	int l=1,r=x[n-1]-x[0],mid,ans=0;
	while(l<=r)
	{
		mid=(l+r)/2;
		if(can(mid))
		{
			ans=mid;l=mid+1;
		}
		else r=mid-1;
	}
	return ans;
}

int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		cin>>x[i];
	}
	sort(x,x+n);
	int ans=maxdist();
	cout<<ans<<endl;
	return 0;
}

(3) There are n items, and each item has a weight w and value v. Now let you select k from the n items, and then get the total unit value of the largest (that is, the total value of k items divided by the total weight).

Problem Solution: This problem cannot be used to directly select the top k items with the highest mass using the greedy algorithm. This will not get the correct result. We have to use the dichotomy method to choose the dichotomous unit value. Whether the condition is satisfied: the unit value of this total is added as m, then for the k items selected among them, the value of each item should be w [i] * m, and then the k items are calculated according to their values The total value is obtained, and then the total value is summed according to the actual value of each. If the actual total value is higher than the total value, then the unit value m is qualified. In this way, the maximum unit value is halved to obtain the final result

 Code:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const double EPS=1e-6;
int n,k;
double w[100005],v[100005];
double c[100005];

bool can(double x)
{
	for(int i=0;i<n;i++)
	{
		c[i]=v[i]-x*w[i];
	}
	sort(c,c+n);
	double p=0;
	for(int i=n-1;i>n-1-k;i--)
	    p+=c[i];
	return p>0;
}

double findmax(double left,double right)
{
	double l=left,r=right,mid,ans=0;
	while(abs(r-l)>EPS)
	{
		mid=(l+r)/2;
		if(can(mid))
		{
			ans=mid;l=mid;
		}
		else r=mid;
	}
	return ans;
}
int main()
{
	cin>>n>>k;
	double maxv=0;
	for(int i=0;i<n;i++)
	{
		cin>>w[i]>>v[i];
		maxv=maxv>v[i]?maxv:v[i];
	}
	double ans=findmax(0.0,maxv);
	cout<<ans<<endl;
	return 0;
}

(4)Copying Books(poj1505)

Sample Input

2
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100

Sample Output

100 200 300 400 500 / 600 700 / 800 900
100 / 100 / 100 / 100 100

Multiple sets of data.

Problem Solution: Two-page pages, from the maximum number of pages in each book to the sum of the pages of all books. The judgment function can require that the right boundary can be moved to meet the requirements of m people, so as to obtain the maximum value of continuous values ​​that meet the requirements. Then in the while loop is the movement of the right boundary every time.

Code:

#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll N = 505;

ll min1, max1;
ll num[N];
bool p[N];
int m, n;

bool can(ll x)
{
	int k = 1;
	ll sum = 0;
	for (int i = 0; i < n; i++)
	{
		sum+=num[i];
		if(sum>x)
		{
			k++;sum=num[i];
		}
	}
	return k<=m; //注意!!! 
}
ll minmax()
{
	ll x = min1;
	ll y = max1;
	ll mid,ans=0;
	while (x <= y)
	{
		mid = (x + y) / 2;
		if (can(mid))
		{
			y = mid-1,ans=mid;   //注意!! 
		}
		else
			x = mid + 1;
	}
	return ans;
}
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		min1 = -1;
		max1 = 0;
		cin >> n >> m;
		for (int i = 0; i < n; i++)
		{
			cin >> num[i];
			if (min1 < num[i])
				min1 = num[i];
			max1 += num[i];
		}
		memset(p, 0, sizeof(p));
		ll ans = minmax();
		ll sum = 0, k = 0;
		for (int i = n - 1; i >= 0; i--)
		{
			if (sum + num[i] <= ans)
				sum += num[i];
			else
			{
				sum = num[i];
				k++;
				p[i] = true;
			}
		}
for (int i = 0; i < n&&k < m - 1; i++)
		{
			if (!p[i])
			{
				p[i] = true;
				k++;
			}
		}
		for (int i = 0; i < n-1; i++)
		{
			cout << num[i] << " ";
			if (p[i])
			{
				cout << "/ ";
			}
		}
		cout << num[n - 1] << endl;
	}

	return 0;
}

The above is my summary of the dichotomy algorithm and some usage.

The dichotomy algorithm is mainly used for two aspects: first, it is to find a specific element in an ordered array; the other is that the solution of the problem is in an interval, and the answer is found through the binary search of this interval. Pay attention to the answer It is required that the answer meets whether there is a continuous interval, and the maximum or minimum value in this interval is taken.

 

Published 6 original articles · liked 0 · visits 184

Guess you like

Origin blog.csdn.net/morning_zs202/article/details/92364209