Brute Force

本文介绍了如何使用递归方法生成1到n的全排列以及非空子集,强调了在生成过程中保持字典序的重要性。同时,讨论了位运算在判断二进制位和回文数问题中的应用,以及如何通过枚举优化计算效率。最后,提到了一个寻找序列中最大乘积的问题,指出该问题与最长连续递增子序列的区别。
摘要由CSDN通过智能技术生成

1.全排列 Permutations of 1~n

输入n,输出按字典顺序lexicographical从小到大的所有1~n的排列。

【思路】使用递归:首先输出以1开头的所有排列,然后是以2开头的,然后是以3开头的...

这类排列问题基本都是字典序输出结果,所以在选择元素放入排列情况的时候一定要按照字典顺序遍历。用数组保存当前排列。

递归:

  1. 终止条件:用一个指针表示当前完成排列的个数,如果n个数都排完了,表示完成了一种排列情况。
  2. 递归部分:没有排完n个数,还需要再选1个数放进数组。怎么选?遍历1到n,找没有在A数组中且最小的那个数,放进数组。
#include<cstdio>
using namespace std;

int A[101];  // n<100

void print_permutation(int n, int* A, int cur) {
  if(cur == n) { // terminate recursion
    for(int i = 0; i < n; i++) printf("%d ", A[i]);
    printf("\n");
  } else for(int i = 1; i <= n; i++) { // try to assign A[cur] with i
    int ok = 1;
    for(int j = 0; j < cur; j++)
      if(A[j] == i) ok = 0; // i is already used in A[0]~A[cur-1]
    if(ok) {
      A[cur] = i;
      print_permutation(n, A, cur+1); // recursion
    }
  }
}

int main() {
  int n;
  scanf("%d", &n);
  print_permutation(n, A, 0); 
  return 0;
}

STL: next_permutation(数组起点,数组终点),左闭右开,结果返回没有排列过的情况的一个数组 

2.集合的所有子集 Generate nonempty proper subsets

描述

Given a set, generate all of its nonvoid proper subsets in ascending order.

输入

Each test case starts with 2 <= N <= 9, which is the number of elements in a set. The next line will have N integers 1 <= ai <= 9, respectively representing the value of each element in the set in ascending order. The input is terminated at the end of the file (EOF).

输出

For each test case, you must print all the nonvoid proper subsets of the input set in ascending order. Afer each test case you must print a blank line.

【思路】输出还是字典序。不包括空集和集合本身。集合本身元素递增。用数组保存当前排列。

对于子集,先找1个元素的集合,再找2个元素的集合.......(增量构建Incremental construction)

对于每个集合,先找最小的元素.......

递归:

        1.终止条件:用一个指针记录当前排列好的元素个数,找到规定数量setNum个元素时,排列完成,输出集合

        2.递归部分:当前个数小于setNum,继续找1个元素。找最小的(正序遍历原集合就可以)。

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

int* a1 = new int[11];
int* a2 = new int[11]; //记录已经选入子集的元素下标

void getSubset(int index, int setNum, int curNum, int N){
	if( setNum==curNum ){
		for(int i=0; i<setNum; i++){
		
			cout << a1[a2[i]];
		}
		cout << '\n';
		return;
	}
	for( ; index<N; ){
		a2[curNum] = index;
		getSubset(++index, setNum, curNum+1, N);
	}
}

int main(){
	int N;
	while( cin>>N ){
		for(int i=0; i<N; i++){
			cin >> a1[i];
		}
		
		
		//子集元素个数从1到N-1
		for(int i=1; i<=N-1; i++){
			getSubset(0, i, 0, N);
		}
		cout << '\n';
		memset(a1, 0, sizeof(int)*11);
		memset(a2, 0, sizeof(int)*11);
	}
}

自己写了一个 

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

int a[11];
int e[11];
int n;

void subset(int num, int cur, int *e){
	if( cur==num ){
		for(int i=0; i<num; i++){
			printf("%d",e[i]);
		}
		printf("\n");
	}
	else{
		for(int i=cur; i<n; i++){
			int isLive = 0;
			for(int j=0; j<cur; j++) 
				if( e[j]==a[i] || e[j]>a[i] ) 
					isLive=1;
			if( isLive==0 ){// 不在e[j]里存在 且满足递增
				e[cur] = a[i];
				subset(num, cur+1, e);
			}
		}
	}
}


int main(){
	while( scanf("%d",&n)!=EOF ){
		
		for(int i=0; i<n; i++){
			scanf("%d", &a[i]);
		}
		// 输入时有序,不用再sort
		for(int i=0; i<n-1; i++){
			memset(e, 0, sizeof(e));
			subset(i+1, 0, e);
		}
		printf("\n");
	}
}

 

3.位运算相关

判断一个数二进制第j位是不是1

if (num & (1 << j))

回文数palindrome numbers

判断一个数的十进制和二进制是否满足回文数

【直接用数组存的,也行吧,就是不怎么位运算】

			//获取num的二进制数 存入数组 下标为0的是最低位
			bFigures = 0;
			dFigures = 0;
			int tmp = num;
			while( tmp!=0 ){
				binArr[bFigures] = tmp%2;
				//cout << binArr[bFigures];
				bFigures++;
				tmp = tmp/2;
			}

			//获取num的十进制数 存入数组 下标为0的是最低位
			tmp = num;
			while( tmp!=0 ){
				decArr[dFigures] = tmp%10;
				//cout << decArr[dFigures];
				dFigures++;
				tmp = tmp/10;
			}
			int isPo = 1;
			for(int i=0; i<dFigures/2; i++){
				if(decArr[i]==decArr[dFigures-i-1]){
					isPo = 1;
					continue;
				}
				else{
					isPo = 0;
					break;
				}
			}

			if( isPo==1 ){
				isPo = 1;
				for(int i=0; i<bFigures/2; i++){
					if(binArr[i]==binArr[bFigures-i-1]){
						isPo = 1;
						continue;
					}
					else{
						isPo = 0;
						break;
					}
				}
				if( isPo==1 ){
					cout << num << endl;
				}
			}
			
			
		}

4.简单枚举  Binary Number (hdu 3711)

Problem Description

For 2 non-negative integers x and y, f(x, y) is defined as the number of different bits in the binary format of x and y. For example, f(2, 3)=1,f(0, 3)=2, f(5, 10)=4. Now given 2 sets of non-negative integers A and B, for each integer b in B, you should find an integer a in A such that f(a, b) is minimized. If there are more than one such integer in set A, choose the smallest one.

f函数是求两个非负二进制数,不相同的位数。有两个集合AB,题目要求获得B集合每个元素对A集合的元素运算f函数的最小值。

【思路】用B的每个元素,去跟A的每个元素做f函数。BF简单,主要是位运算

 

Input

The first line of the input is an integer T (0 < T ≤ 100), indicating the number of test cases. The first line of each test case contains 2 positive integers m and n (0 < m, n ≤ 100), indicating the numbers of integers of the 2 sets A and B, respectively. Then follow (m + n) lines, each of which contains a non-negative integers no larger than 1000000. The first m lines are the integers in set A and the other n lines are the integers in set B.

 

Output

For each test case you should output n lines, each of which contains the result for each query in a single line.

            dif = B[i] ^ A[j]; // 元素异或 获得一个数
			int cnt = 0;
			while( dif!= 0 ){ // 这个数的二进制位为1的个数,就是a,b不同的位数
				cnt += dif & 1;
				dif>>=1;
			}

简单枚举 Division (UVa 725)

Sample Input
61
62
0
Sample Output
There are no solutions for 61.
79546 / 01283 = 62
94736 / 01528 = 62

【思路】核心在于考虑除法特点来尽可能减少枚举次数,如果只是枚举所有的xxxxx/xxxxx序列来找到和输入的商相等的,这样会导致次数过多可能TLE。换除法为乘法,用分子xxxxx和商相乘得到分母,再验证分母每个数是不是都没有出现过。这样处理可以只用枚举分子组合序列,且不会有int/int未整除的情况。


#include<stdio.h>
#include<string.h>

bool bits[10]; //0~9 used or not
//choose = 1, 5 bits; choose = 2, 10 bits
bool check(int num, int choose) 
{
	if(choose == 1)
		memset(bits,0,sizeof(bits));
	int i,j,k,l,m;
	i = num/10000;
	j = num/1000%10;
	k = num/100%10;
	l = num/10%10;
	m = num%10;
	bits[i] = bits[j] = bits[k] = bits[l] = bits[m] = 1;
	int count = 0;
	for(int x=0;x<=9;x++)
		if(bits[x])
			count++;
	if(choose==1 && count<5 || choose==2 && count<10)
		return false;
	return true;
}
// no need to enumerate 10!=3628800
int main()
{
	int n;
	bool flag=false;
	while(scanf("%d",&n) && n!=0)
	{	
		flag=false;
		for(int b=1234;b<=98765;b++)// 分子最小从01234开始
			if(check(b,1) && b*n>10000 && b*n<99999 && check(b*n,2)){
				flag=true;
				printf("%d/%d = %d\n",b*n,b,n);
			}
		if(flag)
			printf("\n");
		else
			printf("There are no solutions for %d.\n",n);			
	}
	return 0;
}

 【思路】根据数的特点减少枚举次数。 

#include<stdio.h>
//#include<string.h>
const int MAXN = 10000; // k
int a[MAXN]; // for all y

int main()
{
	int k;
	while(scanf("%d",&k)==1)
	{
		int count=0;
		for(int y=k+1;y<=2*k;y++) // k+1 <= y <= 2*k
			if((k*y)%(y-k)==0)
			{
				a[count++] = y;
			}
		printf("%d\n",count);
		for(int i=0;i<count;i++)
			printf("1/%d = 1/%d + 1/%d\n", k, (k*a[i])/(a[i]-k), a[i]);
	}
	return 0;
}

5.最大乘积 Maximum Product (UVa 11059)

【注意】 这不是子序列问题,如最长连续递增子序列(元素和,每个元素都是正数,取数不必连续但必须递增),可以用DP解决。这道题要求取数必须连续,且存在负数。

【思路】暴力就是O(n^2) 没想到怎么用dp解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值