枚举
例题:公鸡5文钱一只,母鸡3文钱一只,小鸡3只一文钱,用100文钱买100只鸡,其中公鸡,母鸡,小鸡都必须要有,问公鸡,母鸡,小鸡要买多少只刚好凑足100文钱?
由题意可得方程式:
5x+3y+1/3z = 100
x+y+z = 100
(100>=x,y,z>=0,z%3==0)
代码:
#include <stdio.h>
int main()
{
int i, j, k;
printf("百元买百鸡的问题所有可能的解如下:\n");
for( i=0; i <= 100; i++ )
for( j=0; j <= 100; j++ )
for( k=0; k <= 100; k++ )
{
if( 5*i+3*j+k/3==100 && k%3==0 && i+j+k==100 )
{
printf("公鸡 %2d 只,母鸡 %2d 只,小鸡 %2d 只\n", i, j, k);
}
}
return 0;
}
这种方法需要枚举 101^3 次
然而我们可以根据已知条件进行代码的优化,减少枚举的次数:
三种鸡的和是固定的,我们只需要枚举两种鸡想(x,y),第三种鸡就可以根据约束条件求得(z=100-x-y)
这样就缩小了枚举范围。
另外我们根据方程特点,可以消去一个未知数,可得:
4x+7y=100;
x+y+z=100;
x,y,z>=0,z%3==0,x<=25;
优化后代码可得:
#include <stdio.h>
int main()
{
int x, y, z;
printf("百元买百鸡的问题所有可能的解如下:\n");
for (x = 0; x <= 25; x++)
{
y = 100 - 4 * x;
if (y % 7 == 0 && y >= 0)
{
y /= 7;
z = 100 - x - y;
if(z%3==0&&3*x+5*y+z/3==100)
printf("公鸡 %2d 只,母鸡 %2d 只,小鸡 %2d 只\n",x,y,z);
}
}
return 0;
}
优化之后只需枚举25次
所以能不用枚举就不用枚举!即使真的需要用枚举也要尽量优化!
递推
递推算法分为顺推和逆推两种
顺推
例:斐波那契数列
F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)
逆推:
例:银行存款,年利息是0.03,若要在n年后连本带息取出n元,至少要存多少钱。
太简单就不上代码了。
贪心
题目描述 https://www.luogu.com.cn/problem/P1090
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 1515 为最小的体力耗费值。
输入格式
共两行。
第一行是一个整数 (1≤n≤10000) ,表示果子的种类数。
第二行包含 n 个整数,用空格分隔,第 i 个整数 ai(1≤ai≤20000) 是第i种果子的数目。
输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^31
输入
3
1 2 9
输出
15
说明/提示
对于30%的数据,保证有n≤1000:
对于50%的数据,保证有n≤5000;
对于全部的数据,保证有n≤10000。
#include <iostream>
#include <algorithm>
using namespace std;
int a[10005];
int main()
{
int n, i, j;
cin >> n;
for (i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a+1, a + n+1);
int ans = 0, sum;
for (i = 1; i <= n - 1; i++)
{
sum = a[i] + a[i + 1];
for (j = i + 1; j <= n-1; j++)
{
a[j] = a[j + 1];
if (a[j] > sum)
{
a[j] = sum;
break;
}
}
if (j == n)
a[n] = sum;
ans += sum;
}
cout << ans << endl;
return 0;
}
递归
斐波那契数列(最简单的递归)
F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)
int F(int n)
{
if (n <= 2)
return 1;
return F(n - 1) + f(n - 2);
}
分治
分而治之。
在n个元素中找出最大元素和最小元素。(使用了递归的方式)
void maxandmin(int a[], int i, int j, int& max, int& min)
{
if (i == j)
{
max = min = a[i];
return;
}
if (j - 1 == i)
{
max = a[i] > a[j] ? a[i] : a[j];
min = a[i] < a[j] ? a[i] : a[j];
return;
}
int mid, max1, max2, min1, min2;
mid = (i + j) / 2;//把n个元素分为a1,a2两组
maxandmin(a, i, mid, max1, max2);
maxandmin(a, mid + 1, j, max2, min2);
max = max1 > max2 ? max1 : max2;//分别将这两组的最大最小值相比较
min = max1 < max2 ? max1 : max2;
}
其实就是如果a1和a2中的元素多于两个,则再继续分为两个子集,直至子集中至多有两个元素为止。
构造
根据题意生成符合要求的解
例:输出n个以空格分隔的AC。
for(int i=0;i<n;i++)
{
printf("AC ");
}
printf("AC\n");
复杂的构造往往需要用到其他知识,再次先不做讲解。
模拟
https://www.luogu.com.cn/problem/P1067
题目描述
一元nn次多项式可用如下的表达式表示:
其中,aix^i 称为i次项,ai称为i次项的系数。给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式:
多项式中自变量为xx,从左到右按照次数递减顺序给出多项式。
多项式中只包含系数不为00的项。
如果多项式nn次项系数为正,则多项式开头不出现“+”号,如果多项式nn次项系数为负,则多项式以“-”号开头。
对于不是最高次的项,以“+”号或者“-”号连接此项与前一项,分别表示此项系数为正或者系数为负。紧跟一个正整数,表示此项系数的绝对值(如果一个高于00次的项,其系数的绝对值为11,则无需输出 11)。如果xx的指数大于11,则接下来紧跟的指数部分的形式为“x^bx
b
”,其中 bb为 xx的指数;如果 xx的指数为11,则接下来紧跟的指数部分形式为“xx”;如果 xx 的指数为00,则仅需输出系数即可。
多项式中,多项式的开头、结尾不含多余的空格。
输入格式
输入共有 22 行
第一行11 个整数,nn,表示一元多项式的次数。
第二行有 n+1n+1个整数,其中第ii个整数表示第n-i+1n−i+1 次项的系数,每两个整数之间用空格隔开。
输出格式
输出共 11 行,按题目所述格式输出多项式。
输入输出样例
输入
5
100 -1 1 -3 0 10
输出
100x5-x4+x3-3x2+10
输入
3
-50 0 0 1
输出
-50x^3+1
NOIP 2009 普及组 第一题
对于100%数据,0 ≤n≤100,−100≤系数≤100
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int n,i,a;
cin >> n;//多项式的次数
for (i = n; i>=0; i--)
{
cin >>a;
if (a)//判断a是否为0
{
if (i != n && a > 0)//第一项不用输出+,且只有a为正数时需要输出+号
cout << "+";
if (abs(a) > 1 || i == 0)//当a不为1或-1时输出a
cout << a;
if (a == -1 && i)//当a为-1且i不为0时输出-
cout << "-";
if (i > 1)//两次及两次以上输出指数
cout << "x^" << i;
if (i == 1)//一次项
cout << "x";
}
}
return 0;
}
排序
二分查找
int search(int a[], int n, int target)
{
int m, l = 0, r = n - 1;
while (l < r)
{
m = l + (r - l) / 2;
if (a[m] == target)
return m;
if (a[m] < target)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
}