递归与分治 题目集

分治法求解全排列问题

对每个输入的整数n,用分治法计算并输出1…n的全排列。

void perm(int a[],int x,int y){
	if(x==y){
		fer(i,0,y)cout<<a[i]<<" ";
		cout<<endl;
	}else{
		fer(i,x,y){
			swap(a[i],a[x]);
			perm(a,x+1,y);
			swap(a[i],a[x]);
		}
	}
}
signed main(){
	int a[7];
	fer(i,0,7)a[i]=i+1;
	int n;
	while(cin>>n){
		perm(a,0,n);
	}
	return 0;
}

数的计数

我们要求找出具有下列性质数的个数(包含输入的自然数n)。
先输入一个自然数 n (n≤1000),然后对此自然数按照如下方法进行处理:
1· 不作任何处理,计数
2. 在它的左边加上一个不超过原数的一半的自然数;
3.新的原数是这个加上去的自然数,继续按此规则进行处理,直到不能再加自然数为止。

递归(TLE)

int cnt=0;
void solve(int n){
	cnt+=n/2;
	fer(i,1,n/2+1){
		solve(i);
	}
}
signed main(){
	int n;cin>>n;
	solve(n);
	cout<<cnt;
	return 0;
	
}

记忆化搜索

int a[10000];
void solve(int n){
	if(a[n]!=-1)return;
	a[n]=1;
	fer(i,1,n/2+1){
		solve(i);
		a[n]+=a[i];
	}
}
signed main(){
	
	int n;cin>>n;
	fer(i,1,n+1){
		a[i]=-1;
	}
	solve(n);
	cout<<a[n];
	return 0;
}

分解式的个数

大于1的正整数可以分解成一组因子的乘积,例如12可以分解成:12、6x2、4x3、3x4、3x2x2、2x6、2x3x2、2x2x3,共8个分解式。请设计算法,计算指定的大于1的正整数的分解式的个数。

int cnt;
void solve(int x){
	if(x==1){
		cnt++;return;
	}
	fer(i,2,x+1){
		if(x%i==0){
			solve(x/i);
		}
	}
}
signed main(){
	cf{
		int n;cin>>n;
		cnt=0;
		solve(n);
		cout<<cnt<<endl;	
	}
	return 0;
}

最大公共子序列问题

对序列X=(x1, x2, …, xm),定义其子序列为(xi1, xi2, …, xik),i1 递归 TLE

string a,b;
int solve(int i,int j){
	if(i==-1||j==-1)return 0;
	if(a[i]==b[j])return solve(i-1,j-1)+1;
	else return max(solve(i-1,j),solve(i,j-1));
}
signed main(){
	while(cin>>a>>b){
		int lena=a.size(),lenb=b.size();
		cout<<solve(lena-1,lenb-1)<<endl;
	}
	return 0;
}

动态规划 AC

string a,b;
int cnt[101][101]; 
void solve(int i,int j){
	fer(s,1,i+1){
		fer(k,1,j+1){
			if(a[s-1]==b[k-1]){
				cnt[s][k]=cnt[s-1][k-1]+1;
			}else{
				cnt[s][k]=max(cnt[s-1][k],cnt[s][k-1]);
			}
		}
	}
}
signed main(){
	while(cin>>a>>b){
		fer(i,0,101){
			fer(j,0,101)cnt[i][j]=0;
		}
		int lena=a.size(),lenb=b.size();
		solve(lena,lenb);
		cout<<cnt[lena][lenb]<<endl;
	}
	return 0;
}

快速排序

int a[N];
int partition(int a[],int l,int r){
	int i=l,j=r;
	int x=a[l];
	while(i<j){
		while(i<j&&a[j]>=x)j--;
		a[i]=a[j];
		while(i<j&&a[i]<=x)i++;
		a[j]=a[i];
	}
	a[i]=x;
	return j;
}
void quicksort(int a[],int l,int r){
	int t;
	if(l<r){
		t=partition(a,l,r);
		quicksort(a,l,t-1);
		quicksort(a,t+1,r); 
	}
}
signed main(){
	int n;cin>>n;
	fer(i,0,n)cin>>a[i];
	quicksort(a,0,n-1);
	fer(i,0,n)cout<<a[i]<<" ";
	cout<<endl;
	return 0;
	
}

黑白棋子移动

【题目描述】
有2n个棋子(n≥4)排成一行,开始位置为白子全部在左边,黑子全部在右边,如下图为n=5的情形:
○○○○○●●●●●
移动棋子的规则是:每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。每次移动必须跳过若干个棋子(不能平移),要求最后能移成黑白相间的一行棋子。如n=5时,成为:
○●○●○●○●○●
任务:编程打印出移动过程。

【输入】
输入n。
【输出】
移动过程。
【输入样例】
7
【输出样例】
step 0:ooooooo*******–
step 1:oooooo–o
step 2:oooooo
*–o*
step 3:ooooo–oo
step 4:ooooo
–oo
step 5:oooo–ooo
step 6:oooo
***–ooo*
step 7:ooo–oooo*
step 8:oooo**–ooo*
step 9:o–o**ooooo*
step10:ooo*–oooo
step11:–ooooooo*

char c[N];
int n,blank,cnt;
void print(){
	cout<<"step"<<fixed<<setw(2)<<cnt++<<":";
	fer(i,0,2*n+2)cout<<c[i];
	cout<<endl;
}
void mv(int k){
	fer(j,0,2){
		c[blank+j]=c[k+j];
		c[k+j]='-';
	}
	blank=k;
	print();
}
void solve(int n){
	if(n==3){
		mv(3);
		mv(7);
		mv(1);
		mv(6);
		mv(0);
	}else{
		mv(n);
		mv(2*n);
		solve(n-1);
	}
}
signed main(){
	cin>>n;
	blank=2*n;
	fer(i,0,n)c[i]='o';
	fer(i,n,2*n)c[i]='*';
	c[blank]='-';
	c[blank+1]='-';
	print();
	solve(n-1);
	return 0;
}

hanoi

void hanoi(int n,char a,char b,char c){
	if(n==1)cout<<1<<" "<<a<<" "<<b<<endl;
	else{
		hanoi(n-1,a,c,b);
		cout<<n<<" "<<a<<" "<<b<<endl;
		hanoi(n-1,c,b,a);
	}
}
signed main(){
	int n;cin>>n;
	char a='A',b='B',c='C';
	hanoi(n,a,b,c);
	return 0;
	
}

循环比赛日程表

设有n个选手进行循环比赛,其中n=2m,要求每名选手要与其他n-1名选手都赛一次,每名选手每天比赛一次,循环赛共进行n-1天,要求每天没有选手轮空。
输入
m(m<=10)
输出
表格形式的比赛安排表(数字之间以一个空格分开)
样例输入
3
样例输出
1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1

int a[N][N];
void solve(int a[N][N],int k,int n){
	int m=1;
	fer(s,1,k+1){//1~k
		n=n/2;
		fer(t,1,n+1){//1~n
			fer(i,m+1,2*m+1){
				fer(j,m+1,2*m+1){
					a[i][j+(t-1)*m*2]=a[i-m][j+(t-1)*m*2-m];
					a[i][j+(t-1)*m*2-m]=a[i-m][j+(t-1)*m*2];
				}
			}			
		}
		m*=2;
	}
}
signed main(){
	int k;cin>>k;
	int n=pow(2,k);
	fer(i,1,n+1)a[1][i]=i;
	solve(a,k,n);
	fer(i,1,n+1){
		fer(j,1,n+1){
			cout<<fixed<<setw(3)<<a[i][j];
		}
		cout<<endl;
	}
	return 0;
}

归并排序

#include
using namespace std;
const int N=1e6+10;
int q[N],tmp[N];
void merge_sort(int q[],int l,int r){ //归并排序
	if(l>=r)return;
	int mid=(l+r)>>1;
	merge_sort(q,l,mid),merge_sort(q,mid+1,r); //先递归排序,后归并
	
	int k=0,i=l,j=mid+1;//k是tmp数组的指针
	while(i<=mid&&j<=r){
		if(q[i]<=q[j])tmp[k++]=q[i++];//赋值的同时自增
		else tmp[k++]=q[j++];
	} 
	while(i<=mid)tmp[k++]=q[i++]; //把剩下的大数直接放进tmp数组里
	while(j<=r)tmp[k++]=q[j++];
	for(i=l,j=0;i<=r;i++,j++)q[i]=tmp[j];//把tmp数组里排好序的数放进q数组里
}
int main(){
	int n;
	cin>>n;
	int i;
	for(i=0;i<n;i++){
		scanf("%d",&q[i]);
	}
	
	merge_sort(q,0,n-1);
	
	for(i=0;i<n;i++){
		printf("%d ",q[i]);
	}
	return 0;
}

求逆序对

基于归并排序

const int N=1e5+10;
int n;
int q[N],tmp[N];
typedef long long ll;
ll merge_sort(int q[],int l,int r){
	if(l>=r)return 0;
	int mid=(l+r)/2;
	ll res=merge_sort(q,l,mid)+merge_sort(q,mid+1,r);
	
	int k=0,i=l,j=mid+1;
	while(i<=mid&&j<=r){
	if(q[i]<=q[j])tmp[k++]=q[i++];
	else {
		tmp[k++]=q[j++];
		res+=mid-i+1;
	}
	}
	while(i<=mid)tmp[k++]=q[i++];
	while(j<=r)tmp[k++]=q[j++];
	for(i=l,j=0;i<=r;i++,j++){
		q[i]=tmp[j];
	} 
	return res;
}
int main(){
	cin>>n;
	for(int i=0;i<n;i++)scanf("%d",&q[i]);
	cout<<merge_sort(q,0,n-1)<<endl;
	return 0;
}

二分查找

int a[N];
signed main(){
	int n;cin>>n;
	fer(i,0,n)cin>>a[i];
	int x;cin>>x;
	int l=0,r=n-1;
	int res=-1; 
	while(l<=r){
		int mid=(l+r)>>1;
		if(a[mid]>x)r=mid-1;
		else if(a[mid]==x){
			res=mid;
			r=mid-1;
		}else l=mid+1;
	}
	if(res!=-1)cout<<res+1;
	else cout<<res;
	return 0;
}

平面分割

signed main(){
	int n,p;cin>>n>>p;
	int res=n*(n+1)/2+1;
	p-=2;
	res-=p*(p+1)/2;
	cout<<res; 
	return 0;
}

你可能感兴趣的:(buctoj,算法,c++)