【算法设计与分析】期中考试复习:代码和经典题目 分治、二分、动态规划

写在前面

自用的抱佛脚笔记。
代码可能跟书上不一样。
期中考试的范围:分治法和动态规划。
我的复习范围:
分治:快速排序,归并排序,二分查找,二分模板题(如派)。
动态规划:矩阵相乘,数塔,最长公共子序列,0-1背包。

快速排序

思想:
在数组a中找一个中枢元素x,用两个指针ij遍历数组:i从左往右,j从右往左;一开始i++,当出现a[i]>=x,i停止;j- -,当出现a[j]<=x时,j停止。
此时a[i]>=x,a[j]<=x,而我们需要的是x左边的数小于等于它,x右边的数大于等于它,因此swap(a[i],a[j]),继续执行i++,知道ij相遇。
排完一次之后x左边的数小于等于x,右边的数大于等于x;于是分别对x左边和右边进行快速排序。

#include
using namespace std;
int n;
int a[200000+10];
void quick_sort(int l,int r)
{
	if(l>=r) return;
	int t=(l+r)/2;
	int x=a[t];int i=l-1,j=r+1;
	while(i<j)
	{
		do i++;while(a[i]<x);
		do j--;while(a[j]>x);
		if(i<j)swap(a[i],a[j]);//!!!一定要加if条件 
	}
	quick_sort(l,j);
	quick_sort(j+1,r);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	quick_sort(1,n);
	for(int i=1;i<=n;i++) cout<<a[i]<<" ";
	return 0; 
}

P1177 【模板】快速排序,可以在这里看看自己的快排模板写对没

归并排序

思想:
先一直分治,分到最小。然后开始排序。
取中点mid,由于前面的递归可知,mid左边和mid右边局部有序。
令i从l开始,j从mid+1开始,比较ij哪个小,小的放在新的数组里并指针往前移。
但凡有一个指针走完(i=mid或j=r),break;
则剩下的都是大的,直接接在新数组后。
然后新数组对旧数组赋值即可。

#include
using namespace std;
int n;
int a[200000+10],b[200000+10];
void Merge_sort(int l,int r)
{
	if(l>=r) return;
	int mid=(l+r)/2;
	Merge_sort(l,mid);
	Merge_sort(mid+1,r);
	int i=l,j=mid+1;
	int k=0;
	while(i<=mid&&j<=r)
	{
		if(a[i]<=a[j]) b[k++]=a[i++];
		else b[k++]=a[j++];
	}
	while(i<=mid) b[k++]=a[i++];
	while(j<=r) b[k++]=a[j++];
	
	for(int ii=l,kk=0;ii<=r;ii++,kk++)
	{
		a[ii]=b[kk];
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	Merge_sort(1,n); 
	for(int i=1;i<=n;i++) cout<<a[i]<<" ";
	return 0; 
}

依旧可以通过提交上面的模板题以判断自己写对没。

二分

一般l和r分为这两种(非浮点):

r=mid,l=mid+1;
l=mid,r=mid-1;//那么mid=(l+r+1),否则容易死循环

二分模板
派 经典二分题目
派的AC代码:

#include
#include
using namespace std;
const int N=1e4+10;
int n,f;
double a[N];
const double pi=acos(-1.0);
double sum=0;
double eps=1e-5;
int judge(double mid)
{
	int flag=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]>=mid)
		{
			flag+=a[i]/mid;
		}
	}
	if(flag>=f) return 1;
	else return 0;
}
int main()
{
	int t;cin>>t;
	while(t--)
	{
		cin>>n>>f;
		f++;
		sum=0;
		
		for(int i=1;i<=n;i++)
		{
			int t;cin>>t;
			a[i]=pi*t*t;
			sum+=a[i];
		}
		
		double l=0,r=sum,mid;	
		while(r-l>eps)
		{		
			mid=(l+r)/2;
			if(judge(mid)) l=mid;
			else r=mid;		
		}
		cout.precision(3);
		cout<<fixed<<mid<<endl;
	}	
}

矩阵相乘

【算法设计与分析】期中考试复习:代码和经典题目 分治、二分、动态规划_第1张图片
输入:

5
30 35 15 5 10 20

输出:

11875

代码:

#include
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define fi first
#define se second
#define mem(a,x) memset(a,x,sizeof(a));
#define db double 

//======================
const int N=1e3+10;
int a[N],dp[N][N];//dp[i][j]代表从i到j的最小次数 
int main()
{	
	int n;cin>>n;
	memset(dp,0x3f,sizeof(dp));
	for(int i=0;i<=n;i++) dp[i][i]=0;
	for(int i=0;i<=n;i++) cin>>a[i];
//	for(int i=1;i<=n-1;i++)
//	{
//		dp[i][i+1]=a[i-1]*a[i]*a[i+1];
//	}
//	for(int i=1;i<=n-1;i++)
//	{
//		cout<
//	}
	for(int k=2;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			int j=i+k-1; //结尾 
			for(int z=i;z<j;z++)
			{
				dp[i][j]=min(dp[i][j],dp[i][z]+dp[z+1][j]+a[i-1]*a[z]*a[j]);
			}
		}
			
	}
	cout<<dp[1][n];
	return 0; 
}
/*
5
30 35 15 5 10 20
*/

数塔

【算法设计与分析】期中考试复习:代码和经典题目 分治、二分、动态规划_第2张图片
输入:

5 
7 
3 8 
8 1 0 
2 7 4 4
4 5 2 6 5 

输出:

30

代码:

#include
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define fi first
#define se second
#define mem(a,x) memset(a,x,sizeof(a));
#define db double 

//======================
const int N=105;
int a[N][N],dp[N][N];
int main()
{
	int n;cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			cin>>a[i][j];
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			if(j==1) a[i][j]+=a[i-1][j];
			else if(j==i) a[i][j]+=a[i-1][j-1];
			else a[i][j]+=max(a[i-1][j],a[i-1][j-1]);
		}
	}
	
//	for(int i=1;i<=n;i++)
//	{
//		for(int j=1;j<=i;j++)
//		{
//			cout<
//		}
//		cout<
//	}
	
	int ans=-1;
	for(int i=1;i<=n;i++) ans=max(ans,a[n][i]);
	cout<<ans;
	return 0; 
}

最长公共子序列

讲的很清晰的题解
模板题

#include
using namespace std;
string a,b;
int dp[1005][1005];
int main()
{
	int n,m;cin>>n>>m;
	cin>>a>>b;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
			else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
		}
	cout<<dp[n][m];
	return 0;
}

0-1背包

模板题和一些记录
一个写的很好的01背包题解

未优化的模板(二维):

#include
using namespace std;
int n,m;
int val[1005],wei[1005];
int dp[1005][1005];//dp[i][j]代表前i个物品,背包容量为j的最大价值 
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>wei[i]>>val[i];
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if(j<wei[i]) dp[i][j]=dp[i-1][j];
			else dp[i][j]=max(dp[i-1][j],dp[i-1][j-wei[i]]+val[i]);
		} 
	cout<<dp[n][m];
	return 0;
}

优化过的模板(一维):

#include
using namespace std;
int n,m;
int val[1005],wei[1005];
int dp[1005];//dp[i]代表背包容量为i的最大价值 
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>wei[i]>>val[i];
	
	for(int i=1;i<=n;i++)
		for(int j=m;j>=wei[i];j--)
		{
			dp[j]=max(dp[j],dp[j-wei[i]]+val[i]);
		} 
	cout<<dp[m];
	return 0;
}

偷懒的函数

快排:sort()
二分查找:lower_bound(a,a+n,x),返回第一个大于等于x的地址,想知道位置就要减去a

你可能感兴趣的:(学校专业课,算法,动态规划,排序算法)