难题杂题的思路方法

1发现时间复杂度超的很多就用二分()二分答案非常巧妙

1.1正宗二分

#include<iostream>
#include<stdio.h>
using namespace std;
int erfen(int a[],int left,int right,int x)
{
	int mid;
	while(left<=right)//等于号是为了能取到 
	{
		mid=(right+left)/2;//此处应防止堆栈溢出(当超过int的一般的时候) 
		if(a[mid]>x) right=mid-1;//范围越小越好 
		else if(a[mid]<x) left=mid+1;
		else return mid;
	}
	return -1;
}
int main()
{
	const int n=10;
	int a[n]={1,3,4,6,7,8,10,11,12,15};
	printf("%d %d\n",erfen(a,0,n-1,6),erfen(a,0,n-1,9));
	return 0;
} 

1.2二分答案

/*
题目描述
伐木工人米尔科需要砍倒M米长的木材。这是一个对米尔科来说很容易的工作,因为他有一个漂亮的新伐木机,可以像野火一样砍倒森林。不过,米尔科只被允许砍倒单行树木。

米尔科的伐木机工作过程如下:米尔科设置一个高度参数H(米),伐木机升起一个巨大的锯片到高度H,并锯掉所有的树比H高的部分(当然,树木不高于H米的部分保持不变)。米尔科就行到树木被锯下的部分。

例如,如果一行树的高度分别为20,15,10和17,米尔科把锯片升到15米的高度,切割后树木剩下的高度将是15,15,10和15,而米尔科将从第1棵树得到5米,从第4棵树得到2米,共得到7米木材。

米尔科非常关注生态保护,所以他不会砍掉过多的木材。这正是他为什么尽可能高地设定伐木机锯片的原因。帮助米尔科找到伐木机锯片的最大的整数高度H,使得他能得到木材至少为M米。换句话说,如果再升高1米,则他将得不到M米木材。

输入格式
第1行:2个整数N和M,N表示树木的数量(1<=N<=1000000),M表示需要的木材总长度(1<=M<=2000000000)

第2行:N个整数表示每棵树的高度,值均不超过1000000000。所有木材长度之和大于M,因此必有解。

输出格式
第1行:1个整数,表示砍树的最高高度。

输入输出样例
输入 #1复制
5 20
4 42 40 26 46
输出 #1复制
36
*/
//大模拟技巧(如果实在觉得模拟困难不好理解就用笔算一算,列数字或者画图)
//本题二分的思路告诉我们,不需排序也能二分 ,单调就能做 (二分答案)神奇的将最值问题转化为判定问题 
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
long long tree[1000010];
int n;
long long m;
long long sum=0;
bool check(int mid)//成功的砍伐木头 
{
	for(int i=1;i<=n;i++)
	{
		if(tree[i]>mid) sum+=tree[i]-mid;
	}
	if(sum>=m) return true;
	else return false;
}
int main()
{
	long long left=0;
	scanf("%d %lld",&n,&m);
	long long longe=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&tree[i]);
		longe=max(longe,tree[i]);
	}
	long long mid=(left+longe)/2;
	long long ans=-1;
	while(left<=longe)
	{
		mid=(left+longe)/2;
		if(check(mid))
		{
			ans=mid;
			left=mid+1;
		}
		/*
		else if(sum>m) left=mid+1;
		else break;
		*/
		else longe=mid-1;
		sum=0;
	}
	printf("%lld",ans);
}

2尺取?(俺好像只会打个板子

//抄的大佬的
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<iostream>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<queue>
typedef long long ll;
using namespace std;
 
int main()
{
	int ans,i,j,a[200],sum;
	scanf("%d%d",&n,&max);
	for(i=0;i<n;i++)
		scanf("%d",&a[i]);
	i=0;
	j=0;
	sum=0;
	ans=n+1;
	while(1)
	{
		while(j<n&&sum<max)
			sum+=a[j++];
		if(sum<max)	break;
		ans=min(ans,j-i);
		sum-=a[i++];
	}
	printf("%d\n",ans);
	return 0;
}

3前缀和与差分

//此文件里面的 
//一维前缀和(用于区间求和) (0初始化为0) 
for(int i=2;i<=n;i++) a[i]+=a[i-1]; 
//一维差分
#include<stdio.h>
int main()
{
	int a[1010],b[1010];
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
		int l,r,t;
		scanf("%d %d %d",&l,&r,&t);
		if(t==1)
		{
			b[l]+=p;//l至r有p,r+1以后没有了 
			b[r+1]-=p;
		} 
		else
		{
			b[l]-=p;
			b[r+1]+=p;
		}
	}
	int add=0;
	for(int i=2;i<=n;i++)
	{
		add+=b[i];
		a[i]=a[i-1]+add;
		
	}
	int x,y;
	scanf("%d %d",&x,&y);
	printf("%d",a[y]-a[x-1]);
	return 0;
} 
//二维前缀和 (这个用画图其实最好理解)(第0行第0列初始化为0) 
#include<iostream>
#include<stdio.h>
using namespace std; 
int a[1010][1010];
int main()
{
	int n,m,p;
	scanf("%d %d %d",&n,&m,&p);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
		
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=2;j<=m;j++) a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];
		
	}
	for(int i=1;i<=q;i++)
	{
		int x1,x2,y1,y2;
		scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
		//当需要遍历的时候下面这个语句一定要注意数组越界问题,从2开始否则会访问到0 
		int ans=a[x2][y2]-a[x1-1][y2]-a[x2-1][y1]+a[x1-1][y1-1];//并不是纯图形面积,还有边界的问题 
		printf("%d",ans);
	}
}
//二维差分的根本就是以结尾为开头 
for(int i=0;i<m;i++)
{
	int x1,x2,y1,y2,p;
	scanf("%d %d %d %d %d",&x1,&y1,&x2,&y2);
	b[x1][y1]+=p;b[x2+1][y2+1]+=p;
	b[x2+1][y1]-=p;b[x1][y2+1]-=p;
}

4.数学方法

4.1方程式移项?

//UVA725
/*
Write a program that finds and displays all pairs of 5-digit numbers that between them use the digits 0
through 9 once each, such that the first number divided by the second is equal to an integer N, where
2 ≤ N ≤ 79. That is,
abcde
fghij = N
where each letter represents a different digit. The first digit of one of the numerals is allowed to be
zero.
Input
Each line of the input file consists of a valid integer N. An input of zero is to terminate the program.
Output
Your program have to display ALL qualifying pairs of numerals, sorted by increasing numerator (and,
of course, denominator).
Your output should be in the following general form:
xxxxx / xxxxx = N
xxxxx / xxxxx = N
.
.
In case there are no pairs of numerals satisfying the condition, you must write ‘There are no
solutions for N.’. Separate the output for two different values of N by a blank line.
Sample Input
61
62
0
Sample Output
There are no solutions for 61.
79546 / 01283 = 62
94736 / 01528 = 62
*/ 
//注意几个点1.输出格式要求严格
//2.循环变量初始化
//3.字符数组后有一个'\0'单个字符输出的时候注意呀 
//思路方面就是方程移项 
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
	int a,b,n;
	int hash[10]={0};
	char sa[6],sb[6];
	sa[5]='\0';
	sb[5]='\0';
	int flag2=0;
	while(1)
	{
		flag2++;
		scanf("%d",&n);
		if(flag2!=1&&n!=0) printf("\n");
		if(n==0) break;
		else
		{
			int flag1=0;
			for(int i=1;i<=99999;i++)
			{
				b=i;
				a=b*n;
				int sum1=b;
				int sum2=a;
				if(a>=99999)
				{
					if(flag1==0) 
					{
						printf("There are no solutions for %d.\n",n);
						break;
					}
					else 
					{
						break;
					}
				}
				else
				{
					for(int j=4;j>=0;j--)
					{
						sb[j]=sum1%10+'0';
						hash[sb[j]-'0']++;
						sum1/=10;
					}
					for(int j=4;j>=0;j--)
					{
						sa[j]=sum2%10+'0';
						hash[sa[j]-'0']++;
						sum2/=10;
					}
					int flag=0;
					for(int j=0;j<=9;j++)
					{
						if(hash[j]==0||hash[j]>1)
						{
							flag=1;
							break;
						} 
					}
					fill(hash,hash+10,0);
					if(flag==1) continue;
					else 
					{
						printf("%s / %s = %d\n",sa,sb,n);
						flag1=1;
					}
				}
			}
		}
	}
	return 0;
} 

4.2运用一些限制条件减少搜索分支?

4.3用一些神奇的转换函数将复杂的循环转换为加减法?乘除法?取模运算?

4.4大数高精度的思想

/*这样才是第一层境界*/
/*
L1-046 整除光棍 (20分)
这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s,表示x乘以s是一个光棍,第二个数字n是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。

提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除x为止。但难点在于,s可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。

输入格式:
输入在一行中给出一个不以5结尾的正奇数x(<1000)。

输出格式:
在一行中输出相应的最小的s和n,其间以1个空格分隔。

输入样例:
31
输出样例:
3584229390681 15
#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
typedef long long ll;
int main()
{
	ll n;
	scanf("%lld",&n);
	ll s=1;
	ll ans;
	int weishu=1;
	while(1)
	{
		if(n>s)
		{
			s=s*10+1;
			weishu++;
		}
		else
		{
			if(s%n==0)
			{
				ans=s/n;
				break;
			}
			else 
			{
				s=s*10+1;
				weishu++;
			}
		}
	}
	printf("%lld %d",ans,weishu);
	return 0;
}*/ 
/*这是第二层境界:理解应用的大除法,但是这里变了一下形式除了除法还有%运算,这里需要用到求%运算的性质,也就是说从今往后出现了O(n)n是位数的取模运算时间复杂度*/ 
//确实巧妙,取一位丢一位 
//各种高精度运算其实就是一位一位整的很巧妙 
#include<iostream>
using namespace std;
int main(){
//	freopen("input.txt","r",stdin);
	int n;
	cin>>n;
	int num = 1;
	int cnt = 0;
	while(num < n){
		num = num*10+1;
		cnt++;
	}
	while(true){
		if(num % n==0){
			cout<<num / n;
			cnt++;
			break;
		}else{
			cout<<num/n;
			num %= n;
			num = num*10+1;
			cnt++;
		}
	}
	cout<<" "<<cnt<<endl;
	return 0;
}
 

5用空间换时间

此处没有代码太常用了

6数据特别小的时候直接一个一个遍历(化繁为简)

6.1

/*
数轴上有一条长度为L(L为偶数)的线段,左端点在原点,右端点在坐标L处。有n个不计体积的小球在线段上,开始时所有的小球都处在偶数坐标上,速度方向向右,速度大小为1单位长度每秒。
当小球到达线段的端点(左端点或右端点)的时候,会立即向相反的方向移动,速度大小仍然为原来大小。
当两个小球撞到一起的时候,两个小球会分别向与自己原来移动的方向相反的方向,以原来的速度大小继续移动。
现在,告诉你线段的长度L,小球数量n,以及n个小球的初始位置,请你计算t秒之后,各个小球的位置。

输入
输入的第一行包含三个整数n, L, t,用空格分隔,分别表示小球的个数、线段长度和你需要计算t秒之后小球的位置。
第二行包含n个整数a1, a2, …, an,用空格分隔,表示初始时刻n个小球的位置。

输出
输出一行包含n个整数,用空格分隔,第i个整数代表初始时刻位于ai的小球,在t秒之后的位置。

输入样例1
3 10 5
4 6 8

输出样例1
7 9 9

输入样例2
10 22 30
14 12 16 6 10 2 8 20 18 4

输出样例2
6 6 8 2 4 0 4 12 10 2
*/
//特判一个小球 
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
struct node
{
	int dir;
	int pla;
	int shurupla;
}ball[1000010],temp[1000010];
bool cmp1(node a,node b)
{
	return a.pla<b.pla;
}
bool cmp2(node a,node b)
{
	return a.shurupla <b.shurupla ;
}
int main()
{
	int n,l,t;
	scanf("%d %d %d",&n,&l,&t);
	if(n==1)
	{
		scanf("%d",&ball[1].pla);
		ball[1].dir=1;
		for(int j=1;j<=t;j++)
		{
			if(ball[j].pla==0&&ball[j].dir==-1) ball[j].dir=1;
			if(ball[j].pla==l&&ball[j].dir==1) ball[j].dir=-1;
			ball[j].pla+=ball[j].dir;
		}
		printf("%d",ball[1].pla);
		return 0;
	}
	for(int i=1;i<=n;i++) ball[i].dir=1;
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&ball[i].pla);
		ball[i].shurupla=i;
	}
	sort(ball+1,ball+n+1,cmp1);
	for(int i=1;i<=t;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(j==1)
			{
				if(ball[j].pla==0&&ball[j].dir==-1) ball[j].dir=1;
				if(ball[j].pla==ball[j+1].pla) ball[j].dir=-ball[j].dir;
			}
			else if(j==n)
			{
				if(ball[j].pla==l&&ball[j].dir==1) ball[j].dir=-1;
				else if(ball[j].pla==ball[j-1].pla) ball[j].dir=-ball[j].dir;
			}
			else
			{
				if(ball[j].pla==ball[j-1].pla||ball[j].pla==ball[j+1].pla) ball[j].dir=-ball[j].dir;
			}
		}
		for(int j=1;j<=n;j++) ball[j].pla+=ball[j].dir;
	} 
	sort(ball+1,ball+n+1,cmp2);
	for(int i=1;i<=n;i++)
	{
		if(i==1) printf("%d",ball[i].pla);
		else printf(" %d",ball[i].pla);
	}
	return 0;
}

6.2这道题目真就是硬做好做(枚举加减法的位置即可)

二十四点是一款著名的纸牌游戏,其游戏的目标是使用 3 个加减乘除运算使得 4张纸牌上数字的运算结果为 24。

题目
定义每一个游戏由 4 个从 1-9 的数字和 3 个四则运算符组成,保证四则运算符将数字两两隔开,不存在括号和其他字符,运算顺序按照四则运算顺序进行。其中加法用符号 + 表示,减法用符号 - 表示,乘法用小写字母 x 表示,除法用符号 / 表示。在游戏里除法为整除,例如 2 / 3 = 03 / 2 = 1, 4 / 2 = 2。
老师给了你 n 个游戏的解,请你编写程序验证每个游戏的结果是否为 24 。

输入
从标准输入读入数据。第一行输入一个整数 n,从第 2 行开始到第 n + 1 行中,每一行包含一个长度为 7的字符串,为上述的 24 点游戏,保证数据格式合法。

输出
输出到标准输出。
包含 n 行,对于每一个游戏,如果其结果为 24 则输出字符串 Yes,否则输出字符串 No。

输入样例
10
9+3+4x3
5+4x5x5
7-9-9+8
5x6/5x4
3+5+7+9
1x1+9-9
1x9-5/9
8/5+6x9
6x7-3x6
6x4+4/5

输出样例
Yes
No
No
Yes
Yes
No
No
No
Yes
Yes

样例解释
9+3+4 × 3 = 24
5+4 × 5 × 5 = 105
799+8=3
5 × 6/5 × 4 = 24
3 + 5 + 7 + 9 = 24
1 × 1+99=1
1 × 95/9 = 9
8/5 + 6 × 9 = 55
6 × 73 × 6 = 24
6 × 4 + 4/5 = 24

7two pointers遍历

/*
对于一个 n 维整数向量 v ∈ Zn,其在第 index 个维度上的取值记作 vindex。这里我们约定 index 的取值从 1 开始,即 v = (v1, v2, · · · , vn)。下面介绍一种向量的稀疏表示方法。
如果 v 仅在少量维度上的取值不为 0,则称其为稀疏向量。
例如当 n = 10 时,v = (0, 0, 0, 5, 0, 0,; 3, 0, 0, 1) 就是一个稀疏向量。
由于稀疏向量的非零值较少,我们可以通过仅存储非零值的方式来节省空间。具体来说,每个非零值都可以用一个 (index, value) 对来表示,即该向量在第 index 个维度上的取值 vindex = value ≠ 0。在上面的例子中,v 就可以表示为 [(4, 5), (7, 3), (10, 1)]。
接下来给出这种稀疏表示一般化的定义。
• 对于任意一个 n 维整数向量 v ∈ Zn,如果其在且仅在 a 个维度上取值不为 0,则可以唯一表示为:
[(index1, value1), (index2, value2), · · · , (indexa, valuea)] • 其中所有的 index 均为整数且满足:
1 ≤ index1 < index2 < · · · < indexa ≤ n
• valuei 表示向量 v 在对应维度 indexi 上的非零值。
给出两个 n 维整数向量 u, v ∈ Zn 的稀疏表示,试计算它们的内积。
u · v = ∑ ui · vi

输入
从标准输入读入数据。
输入的第一行包含用空格分隔的三个正整数 n、a 和 b,其中 n 表示向量 u、v 的维数,a 和 b 分别表示两个向量所含非零值的个数。
第二行到第 a + 1 行输入向量 u 的稀疏表示。第 i + 1 行(1 ≤ i ≤ a)包含用空格分隔的两个整数 indexi 和 valuei,表示 uindexi = valuei ≠ 0。 第 a + 2 行到第 a + b + 1 行输入向量 v 的稀疏表示。第 j + a + 1 行(1 ≤ j ≤ b)包含用空格分隔的两个整数 indexj 和 valuej,表示 vindex j = value j ≠ 0

输出
输出到标准输出。
输出一个整数,表示向量 u 和 v 的内积 u · v。

输入样例
10 3 4
4 5
7 -3
10 1
1 10
4 20
5 30
7 40

输出样例
-20

样例解释
u = (0, 0, 0, 5, 0, 0, -3, 0, 0, 1)
v = (10, 0, 0, 20, 30, 0, 40, 0, 0, 0)
u · v = 5 × 20 + (-3) × 40 = 20
*/
#include<cstdio>
#include<vector>
using namespace std;

int n;
int a, b;
long long ans; // 使用 long long  

vector<pair<int, int> > u, v;

int main() {
	scanf("%d%d%d", &n, &a, &b);
	int id, val;
	for(int i=0; i<a; i++) {
		scanf("%d%d", &id, &val);
		u.push_back({id, val});
	}
	
	for(int i=0; i<b; i++) {
		scanf("%d%d", &id, &val);
		v.push_back({id, val});
	}
	
	int i=0, j=0;
	while(i<a && j<b){
		if(u[i].first==v[j].first){
			ans += u[i].second * v[j].second;
			i++; j++;
		}
		else if(u[i].first>v[j].first){
			j++;
		}
		else{
			i++;
		}
	}

	printf("%lld\n", ans);

	return 0;
}

8程序重复度高写模拟的时候头痛?(转换为函数

9举例子法,尝试法,化简问题(比较常用的方法)

10最近找到个好玩的东西:卡特兰数,符合栈的顺序规律

具体证明如下:http://blog.sina.com.cn/s/blog_6917f47301010cno.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值