程序设计与算法二(郭炜)经典例题总结(持续更新中)

递归

例题2->Hanoi塔

#include<iostream>
using namespace std;

void Move(char a, char b)
{
	cout << a << "->" << b << endl;
}

void Hanoi(int n, char a, char b, char c)
{
	if (n == 1)
		Move(a, c);
	else
	{
		Hanoi(n - 1, a, c, b);//将A座上的n-1个盘子借助C座移向B座
		Move(a, c);//将A座上最后一个盘子移向C座
		Hanoi(n - 1, b, a, c);//将B座上的n-1个盘子借助A座移向C座
	}
}

int main()
{
	int n;
	cout<< "input how many plates:";
	cin >> n;//输入共有几个待移动盘子
	Hanoi(n, 'a', 'b', 'c');
}

例题3->N皇后

#include<iostream>
#include<cmath>
using namespace std;
int QueenPos[100];
int N;//N是递归中的一个变量所以要定义在全局变量中
int Terms;//Terms是递归中的一个变量所以要定义在全局变量中

void Nqueen(int n)
{
	if (n == N)//当摆放好的皇后数与总的皇后数一样时,则证明现在有一种可行的方案,输出
	{
		for (int i = 0; i < N; i++)
			cout << i << ":" << QueenPos[i]<<" ";
		cout << endl;
		Terms++;//计数共有几种可行的方案
		return;
	}
	for (int i = 0; i < N; i++)//逐个试探第n个皇后的位置,从0~N
	{
		int j;
		for (j = 0; j < n; j++)//检查若第n个皇后在第i个位置会不会与前n个皇后中的某个冲突
		{
			if (QueenPos[j] == i || abs(QueenPos[j] - i) == n - j)
			{
				break;//若冲突,退出此循环
			}
		}
		if (n == j)//若成立则说明此皇后所在位置符合要求
		{
			QueenPos[n] = i;
			Nqueen(n + 1);//为下一个皇后分配位置
		}
	}
}

int main()
{
	cout << "input how many queens:";
	cin >> N;
	Nqueen(0);
	cout << Terms << " available configurations" << endl;
}

 例题4->逆波兰表达式

#include<iostream>
#include<cstdlib>
using namespace std;

int Exp()
{
	char ch[100];
	cin >> ch;

	switch (ch[0])
	{
		case '+': return Exp() + Exp();
		case '-': return Exp() - Exp();
		case '*': return Exp() * Exp();
		case '/': return Exp() / Exp();
		default : return atoi(ch);//将字符串型转变为整数型
		break;
	}
}
int main()
{
	cout << Exp() << endl;
}

补充知识:

在C++的cstdlib中,除了atof()之外,还有其他几个与字符串到数值转换相关的函数。这些函数包括:

  1. atoi(): 将字符串转换为整数(int)。
  2. atol(): 将字符串转换为长整数(long)。
  3. atoll(): 将字符串转换为长长整数(long long),这个函数在C99标准中引入。
  4. atof(): 将字符串转换为浮点数(double)。

例题5->表达式

#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;

int Factor_Value();
int Term_Value();
int Expression_Value();

int Expression_Value()//处理整个表达式中的=-运算
{
	int Res = Term_Value();//先计算表达式中的项(如果有的话)
	bool more = 1;

	while (more)
	{
		char Ini = cin.peek();//从现存的表达式中提取第一个因子(符号)
		if (Ini == '+' || Ini == '-')//通过刚才判断的符号判断接下来还有没有项
		{
			cin.get();//舍弃刚刚用来判断的‘+’或‘-’
			int Leftover = Term_Value();//对这个项进行计算
			if (Ini == '+')//将上述得到的两项计算
				Res += Leftover;
			else
				Res -= Leftover;
		}
		else
			more = 0;
	}
	return Res;
}

int Term_Value()//处理表达式中的项,即(a*/b)
{
	int Res = Factor_Value();//判断这项的第一个因子
	while (1)
	{
		char Ini = cin.peek();//从现存的表达式中提取第一个因子(符号)
		if (Ini == '*' || Ini == '/')//通过刚才判断的符号判断接下来还有没有需要运算的因子
		{
			cin.get();
			int Leftover = Factor_Value();
			if (Ini == '*')
				Res *= Leftover;
			else
				Res /= Leftover;
		}
		else
			break;
	}
	return Res;
}

int Factor_Value()//处理每一个因子并且计算最后的值
{
	int Res = 0;
	char ch = cin.peek();

	if (ch == '(')//如果当前表达式第一个因子为'(',说明此时需要计算一个子表达式
	{
		cin.get();//舍弃此'(‘
		Res = Expression_Value();//返回计算这个子表达式
		cin.get();//舍弃遗存的')'
	}
	else
	{
		while (isdigit(ch))
		{
			Res = 10 * Res + ch - '0';//计算最终结果,说明:数字的ascii-0的ascii即是这个数
			cin.get();
			ch = cin.peek();
		}
	}
	return Res;
}

int main()
{
	cout << Expression_Value() << endl;
}

补充知识:

cin.get() 是 C++ 中用于从输入流 cin 中读取一个字符的函数。它的作用类似于 cin >> 操作符,但是不同之处在于 cin.get() 可以读取输入流中的任何字符,包括空格、制表符甚至换行符,而不会像 >> 操作符一样被空白字符忽略。

cin.peek() 是 C++ 中用于查看输入流 cin 中下一个字符的函数。它的作用是返回输入流中下一个字符的值,但不会从输入流中移除该字符,也不会导致程序执行在字符上停止。这意味着可以使用 cin.peek() 来查看下一个字符,然后决定如何处理输入,而不会影响后续的输入操作。

关于几个判断函数:

  • isalpha(c):检查字符 c 是否是字母(a-z 或 A-Z)。
  • isdigit(c):检查字符 c 是否是数字字符(0-9)。
  • isalnum(c):检查字符 c 是否是字母或数字字符。
  • islower(c):检查字符 c 是否是小写字母。
  • isupper(c):检查字符 c 是否是大写字母。
  • isspace(c):检查字符 c 是否是空白字符(空格、制表符、换行符等)。
  • ispunct(c):检查字符 c 是否是标点符号。
  • isxdigit(c):检查字符 c 是否是十六进制数字(0-9、a-f、A-F)。

例题6->爬楼梯

#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;

int Stairs(int N)
{
	if (N < 0)//边界条件
		return 0;
	else if (N == 0)//边界条件
		return 1;
	else
		return Stairs(N - 1) + Stairs(N - 2);
}

int main()
{
	int N;
	cin >> N;
	cout << "爬楼梯有" << Stairs(N) << "方式" << endl;
}

例题7->分苹果(待修改)

#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;

int Apples(int m, int n)
{
	if (m < n)//边界条件
		return Apples(m, m);
	else if (n == 0)//边界条件
		return 0;
	else if (m == 0)
		return 1;
	else
		return Apples(m, n - 1) + Apples(m - n, n);
}

int main()
{
	int m, n;
	cin >> m >> n;
	cout << "total " << Apples(m, n) << " ways to allocate the apples" << endl;
}

例题8->算24

#include<iostream>
using namespace std;
#define EPS 1e-6//由于浮点数之间的是否相等比较不能用 a==b 这种方法来判断,所以需要加一个极小值来充当判断条件

bool IsZero(double x)//判断某个数是否为0(x即为两个浮点数相减的结果)
{
	return fabs(x) <= EPS;
}

bool Count24(double Arr[], int n)
{
	if (n == 1)//当Arr[]中只剩下一个值才能进入最终的判断条件
	{
		if (IsZero(Arr[0] - 24))//最终的判断的条件,最后计算的只会剩下一个值,即Arr[0]
			return 1;
		else
			return 0;
	}

	double arr[4];//定义一个用来拷贝Arr[]值的数组
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)//二重循环为了枚举所有的2个取值
		{
			int flag = 0;//arr[]的计数器
			for (int k = 0; k < n; k++)//这个循环是为了先将Arr[]中的剩下的值存到arr[]中
			{
				if (k != i && k != j)
				{
					arr[flag++] = Arr[k];
				}
			}

			arr[flag] = Arr[i] + Arr[j];//将从arr[]枚举出来的两个值进行各种运算,然后存到arr[]中
			if (Count24(arr, n - 1))
				return 1;
			arr[flag] = Arr[i] - Arr[j];
			if (Count24(arr, n - 1))
				return 1;
			arr[flag] = Arr[j] - Arr[i];
			if (Count24(arr, n - 1))
				return 1;
			arr[flag] = Arr[i] * Arr[j];
			if (Count24(arr, n - 1))
				return 1;
			if (!IsZero(Arr[j]))//判断除数是否为0
			{
				arr[flag] = Arr[i] / Arr[j];
				if (Count24(arr, n - 1))
					return 1;
			}
			if (!IsZero(Arr[i]))
			{
				arr[flag] = Arr[j] / Arr[i];
				if (Count24(arr, n - 1))
					return 1;
			}
		}
	}
	return 0;//若直到最后都没有符合条件,那么什么没有符合的值
}

int main()
{
	double Arr[4];
	for (int i = 0; i < 4; i++)
		cin >> Arr[i];
	if (Count24(Arr, 4))
		cout << "solution exists" << endl;
	else
		cout << "solution not exists" << endl;
}

划重点:由于浮点数之间的是否相等比较不能用 a==b 这种方法来判断,所以需要加一个极小值EPS来充当判断条件,即通过判断fabs(a-b)是否小于EPS.

二分算法

例题1->二分查找 

#include <iostream>
using namespace std;

void BinarySearch(int n)
{
    int Arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int low = 0;
    int high = sizeof(Arr) / sizeof(int) - 1;

    if (cin.fail()) {
        cout << "Invalid input. Please input a valid number." << endl;
        return;
    }

    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (Arr[mid] < n)
            low = mid + 1;
        else if (Arr[mid] > n)
            high = mid - 1;
        else {
            cout << "Found it! Index is " << mid << endl;
            return;
        }
    }

    cout << "No such number." << endl;
}

int main()
{
    int n;
    cout << "Input the number you want to find: ";
    cin >> n;
    BinarySearch(n);
    return 0;
}

补充知识:

cin.fail() 是C++中用于检查输入流的状态的函数。当使用 cin 进行输入时,如果输入的数据类型与预期不符或者输入过程中发生错误,cin.fail() 函数将返回 true,表示输入流处于错误状态。通常,它用于检查用户输入了非预期的数据类型,比如当用户输入了一个字母而不是数字。当 cin.fail() 返回 true 时,你就可以处理输入错误的情况,比如清空输入流、提示用户重新输入等。

例题2->二分法求方程的根

#include <iostream>
using namespace std;
#define EPS 1e-6

bool IsZero(double x)//判断是否达到精确值
{
	return fabs(x) <= EPS;
}

double Func(double x)//计算函数值
{
	return x * x * x - 5 * x * x + 10 * x - 80;
}

void Root()
{
	double x1 = 0, x2 = 100, mid;//在0~100的定义域内进行逼近
	int flag = 0;//计数进行了几次逼近
	while (1)
	{
		flag++;
		mid = (x1 + x2) / 2;
		if (IsZero(Func(mid)))//已经逼近到精确值,退出循环
			break;
		else if (Func(mid) > EPS)//理解为此时函数值大于0,右值向左移动
			x2 = mid;
		else//相反左值向右移动
			x1 = mid;
	}
	cout << mid << " is the root with " << flag << " times binary search" << endl;
}

int main()
{
	Root();
}

例题3->找一对数

#include <iostream>
using namespace std;
#define TOTAL 66//规定两个数之和为66

void Pair()
{
	int Arr[20] = { 0, 10, 12, 17, 19, 25, 28, 31, 35, 42, 49, 51, 57, 60, 66, 73, 83, 88, 94, 95 };
	int another, low, high, mid;
	for (int i = 0;i < 20; i++)//先遍历第一个值
	{
		another = TOTAL - Arr[i];//算出第二个值作为下面二分查找的目标
		low = 0;
		high = 19;
		while (low <= high)
		{
			mid = low + (high - low) / 2;
			if (another < Arr[mid])
				high = mid - 1;
			else if (another > Arr[mid])
				low = mid + 1;
			else
			{
				cout << "find it:" << Arr[mid] << " and " << Arr[i] << endl;
				return;
			}
		}
	}
	cout << "can not find matching number" << endl;
}

int main()
{
	Pair();
}

例题4->农夫与奶牛(待补)

分治

例题1->归并排序

#include <iostream>
using namespace std;

int Arr[20] = { 57, 12, 35, 88, 42, 66, 19, 73, 25, 51, 94, 17, 60, 7, 49, 10, 31, 83, 28, 95 };
int Temp[20] = {0};//拷贝数组,存放临时的排序后的数组内容

void Merge(int Arr[], int s, int m, int e, int Temp[])
{
	int p1 = s, p2 = m + 1, p = 0;
	while (p1 <= m && p2 <= e)
	{
		if (Arr[p1] < Arr[p2])//按照大小依次将第一个或者第二个数组的值放入Arr[]中
			Temp[p++] = Arr[p1++];
		else if (Arr[p1] > Arr[p2])
			Temp[p++] = Arr[p2++];
	}
	while (p1 <= m)//如果第一个子数组还有内容而第二个子数组没有内容,则把第一个子数组中的剩下的值依次放入Temp[]中
		Temp[p++] = Arr[p1++];
	while (p2 <= e)
		Temp[p++] = Arr[p2++];
	for (int i = s; i <= e; i++)//把排好序的两个子数组放回原来的数组的对应位置
	{
		Arr[i] = Temp[i - s];
	}
}

void MergeSort(int Arr[], int s, int e, int Temp[])//用来细分从s到e的这段数组,用Merge()函数来排序然后合并细分后的小数组
{
	int m = 0;
	if (s < e)
	{
		m = s + (e - s) / 2;//分隔数组为两个子数组
		MergeSort(Arr, s, m, Temp);//两个子数组再重复递归分为更小的子数组
		MergeSort(Arr, m + 1, e, Temp);
		Merge(Arr, s, m, e, Temp);//排序,合并各级子数组
	}
}

int main()
{
	int s, e;
	cout << "input start and end point:";
	cin >> s >> e;
	MergeSort(Arr, s, e, Temp);
	for (int i = s; i <= e; i++)
		cout << Arr[i] << " ";
}

 例题2->快速排序

#include <iostream>
using namespace std;

int Arr[20] = { 57, 12, 35, 88, 42, 66, 19, 73, 25, 51, 94, 17, 60, 7, 49, 10, 31, 83, 28, 95 };

void Swap(int& a, int& b)//用于在快速排序中的交换两值操作
{
	int temp = a;
	a = b;
	b = temp;
}

void QuickSort(int s, int e)
{
	if (s >= e)//若起始点大于等于终止点,则返回
		return;
	if (cin.fail())//若输出不正确,返回
		cout << "invaild input" << endl;

	int low = s, temp = Arr[low], high = e;

	while (low != high)//运行到最后一定有low=high
	{
		if (high > low && Arr[high] > temp)//先从temp的右边向左边搬运小于temp的值
			high--;//若temp右边的值仍然大于temp,则high指针向左移动
		Swap(Arr[low], Arr[high]);//发现有右边的值小于temp的值,则交换
		if (high > low && Arr[low] < temp)//只后从temp的左边向右边搬运小于temp的值
			low++;//若temp左边的值仍然小于temp,则high指针向右移动
		Swap(Arr[high], Arr[low]);//发现有左边的值大于temp的值,则交换
	}

	QuickSort(s, low - 1);//递归在当前temp的左边进行重复的交换操作
	QuickSort(low + 1, e);
}

int main()
{
	int s, e;
	cout << "input start and end:";
	cin >> s >> e;//输入起始点和终止点

	QuickSort(s, e);
	for (int i = s; i <= e; i++)
		cout << Arr[i] << " ";
}

动态规划

例题1->数字三角形(1)

#include <iostream>
using namespace std;
int Dis[6][6] = 
{
	{1, 0, 0, 0, 0, 0},
	{7,  8, 0, 0, 0, 0},
	{13, 14, 15, 0, 0, 0},
	{19, 20, 21, 22, 0, 0},
	{25, 26, 27, 28, 29, 0},
	{31, 32, 33, 34, 35, 36}
};
int Max[6][6];

int FindMax(int i, int j)
{
	if (Max[i][j] != -1)//此处即是动态规划的体现,每次将已经计算好的Max值存起来,不用再次计算,大大的减少了计算
		return Max[i][j];
	if (i == 6)//若计算到最后一行,则返回本身的值
		Max[i][j] = Dis[i][j];
	else
		Max[i][j] = max(FindMax(i + 1, j), FindMax(i + 1, j + 1)) + Dis[i][j];//否则在左下或右下值中选择一个较大的
	return Max[i][j];
}

int main()
{
	for (int i = 0; i < 6; i++)
		for (int j = 0; j <= i; j++)
			Max[i][j] = -1;
	cout << "the max distance is " << FindMax(0, 0) << endl;
}

例题2->数字三角形(2)

#include <iostream>
using namespace std;
int Dis[6][6] = 
{
	{1, 0, 0, 0, 0, 0},
	{7,  8, 0, 0, 0, 0},
	{13, 14, 15, 0, 0, 0},
	{19, 20, 21, 22, 0, 0},
	{25, 26, 27, 28, 29, 0},
	{31, 32, 33, 34, 35, 36}
};

int FindMax(int i, int j)
{
	for (int i = 4; i >= 0; i--)
		for (int j = 0; j <= i; j++)
			Dis[i][j] = max(Dis[i + 1][j], Dis[i + 1][j + 1]) + Dis[i][j];
	return Dis[0][0];
}

int main()
{
	cout << "the max distance is " << FindMax(0, 0) << endl;
}

例题3->最长上升子序列

#include <iostream>
using namespace std;
int Arr[6];

int GetMax(int MaxLen[])
{
	int temp = MaxLen[0];
	for (int i = 0; i < 6; i++)
	{
		if (MaxLen[i] > temp)
		{
			temp = MaxLen[i];
		}
	}
	return temp;
}

void MaxArray(int Arr[])
{
	int MaxLen[6];
	for (int i = 0; i < 6; i++)
	{
		cout << "input data:";
		cin >> Arr[i];
		MaxLen[i] = 1;
	}


	for (int i = 1; i < 6; i++)//为MaxLen数组赋值
	{
		for (int j = 0; j < i; j++)//通过遍历i之前的值,看看当前Arr[i]是否大于前面的所有值
		{
			if (Arr[i] > Arr[j])//如果是,则将新值写入MaxLen[]中
			{
				MaxLen[i] = max(MaxLen[i], MaxLen[j] + 1);//如果Arr[i]也是当前最大递增序列中的,则MaxLen[i]是上一个最大递增序列的MaxLen+1
			}
		}
	}

	cout << "Max length is " << GetMax(MaxLen);
}

int main()
{
	MaxArray(Arr);
}

例题4->最长公共子序列(待补)

例题5->最佳加法表达式(待补)

例题6->help jimmy(待改)

#include <iostream>
#include<limits>
using namespace std;
#define MAXHEIGHT 3
#define MAXTIME numeric_limits<double>::max()
#define MAX 100

struct Point
{
	int h;
	int x;
}p[3];
int Time[10][10];
int LJump(int s, int e);
int RJump(int s, int e);

int LJump(int s, int e)
{
	if (e == 2)
	{
		if (p[2].h >= MAXHEIGHT)
			return MAXTIME;
		else
			return p[2].h;
	}
	else
	{
		for (int i = s; i < 3; i++)
		{
			if (p[s].h - p[i].h < MAXHEIGHT)
			{
				if (Time[s][e] != -1)
					return Time[s][e];
				else
				{
					Time[s][e] = p[s].h - p[e].h + min(LJump(e, i) + p[e].x - p[i].x, RJump(e, i) + p[i].x - p[e].x);
					return Time[s][e];
				}
			}
		}
	}
}

int RJump(int s, int e)
{
	if (e == 2)
	{
		if (p[2].h >= MAXHEIGHT)
			return MAXTIME;
		else
			return p[2].h;
	}
	else
	{
		for (int i = s; i < 3; i++)
		{
			if (p[s].h - p[i].h < MAXHEIGHT)
			{
				if (Time[s][e] != -1)
					return Time[s][e];
				else
				{
					Time[s][e] = p[s].h - p[e].h + min(LJump(e, i) + p[e].x - p[i].x, RJump(e, i) + p[i].x - p[e].x);
					return Time[s][e];
				}
			}
		}
	}
}

int main()
{
	memset(Time, -1, sizeof(Time));
	for (int i = 0; i < 3; i++)
	{
		cout << "input h and x:";
		cin >> p[i].h >> p[i].x;
	}
	cout << LJump(0, 1) << endl;
}

例题7->滑雪

#include <iostream>
using namespace std;

int Terr[5][5] = 
{
    {1, 2, 3, 4, 5},
    {16, 17, 18, 19, 6},
    {15, 24, 25, 20, 7},
    {14, 23, 22, 21, 8},
    {13, 12, 11, 10, 9}
};

void Ski(int l[5][5])
{
    for (int i = 0; i < 5; i++)//循环地图每一个点
    {
        for (int j = 0; j < 5; j++)
        {
            if (i - 1 >= 0 && i + 1 <= 5 && j - 1 >= 0 && j + 1 <= 5)//避免越界
            {
                if (Terr[i - 1][j] > Terr[i][j])//检查每个点四周的高低情况
                {
                    l[i - 1][j] = max(l[i - 1][j], l[i][j] + 1);//若四周有点位置比当前点高,此时有两种情况
                }//1:更新四周点的最远距离,即+1   2:该四周点当前最远滑雪距离与前者相比更远,无需更新
                if (Terr[i + 1][j] > Terr[i][j])
                {
                    l[i + 1][j] = max(l[i + 1][j], l[i][j] + 1);
                }
                if (Terr[i][j - 1] > Terr[i][j])
                {
                    l[i][j - 1] = max(l[i][j - 1], l[i][j] + 1);
                }
                if (Terr[i][j + 1] > Terr[i][j])
                {
                    l[i][j + 1] = max(l[i][j + 1], l[i][j] + 1);
                }
            }
        }
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            cout << l[i][j] << " ";
        }
        cout << endl;
    }
}

int main()
{
    int l[5][5];
    for (int i = 0; i < 5; i++)//将每个点可以滑雪的最长路径置为1
        for (int j = 0; j < 5; j++)
            l[i][j] = 1;
    Ski(l);
}

 例题8->神奇的口袋

#include <iostream>
using namespace std;
int good[20] = { 3, 11, 7, 0, 14, 9, 16, 5, 10, 2, 8, 1, 12, 4, 15, 6, 13, 10, 0, 9 };

void Weight()
{
	int Ways[21][41];//Ways[x][y]:从前x个物品里面找质量为y的方法数
	memset(Ways, 0, sizeof(Ways));//所有方法数置为0
	for (int i = 0; i < 21; i++)
		Ways[i][0] = 1;//前x个物品里面找质量为0的方法数有1个

	for (int i = 1; i < 41; i++)
	{
		for (int j = 1; j < 21; j++)
		{
			Ways[j][i] = Ways[j - 1][i];//前j个物品里面找质量为i的方法数首先包含:前j-1个物品里面找质量为i的方法数
			//注意此时方法数里面不包含第j个物品
			if (i >= good[j])//若方法数里面可以包含第i个物品(因为第j个物品可能质量大于i)
				Ways[j][i] += Ways[j - 1][i - good[j]];//包含第j个物品,那么还要填充i-good[j]质量的物品
				//填充余下质量的方法数即是包含第j个物品时达到质量i的方法数
		}
	}

	cout << "total " << Ways[20][40] << " ways" << endl;
}

int main()
{
	Weight();
}

补充知识:在C++中,memset()函数通常用于将一块内存的内容设置为指定的值。一般情况下,不建议直接在C++中使用memset()来处理数组越界的情况,因为memset()主要用于设置内存块的连续值,而不对数组进行越界检查。

函数原型如下:void *memset(void *str, int c, size_t n);

例题9->0-1背包问题(待优化)

#include <iostream>
using namespace std;

struct Good
{
	int Value;
	int Volume;
}good[11];

void MaxValue(Good good[])
{
	int value[11][51];
	memset(value, 0, sizeof(value));

	for (int j = 1; j < 51; j++)//先为value[1][j]取边界值
	{
		if (good[1].Volume > j)//若第一个物品的体积大于j体积,说明放不进去,所以前1个物品不超过体积j的最大价值为0
			value[1][j] = 0;
		else//否则为第一个物品的价值
			value[1][j] = good[1].Value;
	}

	for (int i = 1; i < 11; i++)
	{
		for (int j = 1; j < 51; j++)
		{
			if (good[i].Volume > j)//若第i个物品的体积大于j,则说明前i个物品的不超过体积j的最大加价值为前i-1个物品的   
				value[i][j] = value[i - 1][j];
			else//否则判断是带第i个物品的最佳价值高还是不带的价值高
				value[i][j] = max(value[i - 1][j], value[i - 1][j - good[i].Volume] + good[i].Value); 
		}
	}

	cout << "max value is " << value[10][50];
}

int main()
{
	for (int i = 1; i < 11; i++)
	{
		good[i].Value = pow((i + 1), 2) - i * 3;
		good[i].Volume = pow((i + 2), 2) - i * 6;
	}
	MaxValue(good);
}

例题10->方盒游戏(待补)

#include <iostream>
#include<string>
using namespace std;
const int n = 3;
int Score[n][n][n];

struct Segment
{
	int Color;
	int Len;
}seg[n];

int Game(int s, int e, int len)
{
	if (Score[s][e][len] != -1)
		return Score[s][e][len];
	int res = pow(seg[e].Len + len, 2);
	if (s == e)
		return res;

	res += Game(s, e - 1, 0);

	for (int i = s; i < e; i++)
	{
		if (seg[i].Color != seg[e].Color)
			continue;
		int temp = Game(i + 1, e - 1, 0);
		temp += Game(s, i, seg[e].Len + len);
		res = max(res, temp);
	}
}

int main()
{
	memset(Score, -1, sizeof(Score));
	int lastc = -1;
	int c, index = -1;

	for (int i = 0; i < n; i++)
	{
		cin >> c;
		if (lastc != c)
		{
			index++;
			seg[index].Color = c;
			seg[index].Len = 1;
			lastc = c;
		}
		else
			seg[index].Len++;
	}

	cout << Game(0, index, 0);
}

深度优先搜索

例题1->寻找路径(待改)

#include <iostream>
using namespace std;

struct Graph
{
	int D[20];
	int M[20][20];
	int Vnum, Enum;
};

bool  CheckRepeat(int Arr[], int e, int temp)//检查是否有重复的值
{
	for (int i = 0; i < e; i++)
	{
		if (Arr[i] == temp)
			return 1;
	}
	return 0;
}

Graph CreateG()//生成无向图
{
	Graph g;
	memset(g.M, 0, sizeof(g.M));

	cout << "input Vnum and Enum:";
	cin >> g.Vnum >> g.Enum;
	int temp;

	for (int i = 0; i < g.Vnum; i++)
	{
		cout << "input data:";
		cin >> temp;
		while (cin.fail() || CheckRepeat(g.D, i, temp))//检查是否有输入错误或者重复输入
		{
			cout << "invalid input, input again:";
			cin.clear();
			cin.ignore(numeric_limits<streamsize>::max(), '\n'); 
			cin >> temp;
		}
		g.D[i] = temp;
	}

	int s, e, w;
	for (int i = 0; i < g.Enum; i++)
	{
		cout << "input start and end(index):";
		cin >> s >> e;
		cout << "input weight:";
		cin >> w;
		g.M[s][e] = g.M[e][s] = w;
	}

	return g;
}

bool DFS(Graph g, int s, int e, int *visit)
{
	if (s == e)//若起点与终点相同,说明已经到达终点
	{
		return 1;
	}
	if (visit[s])//若此点已经被遍历过,直接返回
		return 0;
	visit[s] = 1;//若没有被遍历过,标记为1

	for (int i = 0; i < g.Vnum; i++)//检查与此点相邻的点继续遍历
	{
		if (g.M[s][i])
		{
			if (DFS(g, i, e, visit))
			{
				cout << i << " ";//输出轨迹
				return 1;//一旦到达终点,就逐层返回1
			}
			else
				continue;//若遍历后没到达终点,继续进行
		}
	}
	return 0;
}

void Search(Graph g)
{
	int s,e,visit[20];
	cout << "input start point and end point:";
	cin >> s >> e;//输入起点下标
	memset(visit, 0, sizeof(visit));//将状态矩阵置0
	if (DFS(g, s, e, visit))
		cout << endl;
	else
		cout << "no such way" << endl;
}

int main()
{
	Graph g = CreateG();
	Search(g);
}

例题2->深度优先遍历

#include <iostream>
using namespace std;

struct Graph
{
	int D[20];
	int M[20][20];
	int Vnum, Enum;
};

bool  CheckRepeat(int Arr[], int e, int temp)//检查是否有重复的值
{
	for (int i = 0; i < e; i++)
	{
		if (Arr[i] == temp)
			return 1;
	}
	return 0;
}

Graph CreateG()//生成无向图
{
	Graph g;
	memset(g.M, 0, sizeof(g.M));

	cout << "input Vnum and Enum:";
	cin >> g.Vnum >> g.Enum;
	int temp;

	for (int i = 0; i < g.Vnum; i++)
	{
		cout << "input data:";
		cin >> temp;
		while (cin.fail() || CheckRepeat(g.D, i, temp))//检查是否有输入错误或者重复输入
		{
			cout << "invalid input, input again:";
			cin.clear();
			cin.ignore(numeric_limits<streamsize>::max(), '\n'); 
			cin >> temp;
		}
		g.D[i] = temp;
	}

	int s, e, w;
	for (int i = 0; i < g.Enum; i++)
	{
		cout << "input start and end(index):";
		cin >> s >> e;
		cout << "input weight:";
		cin >> w;
		g.M[s][e] = g.M[e][s] = w;
	}

	return g;
}

void DFS(Graph g, int s, int *visit)
{
	if (visit[s])//若此点已经被遍历过,直接返回
		return;
	visit[s] = 1;//若没有被遍历过,标记为1并输出
	cout << g.D[s] << " ";
	for (int i = 0; i < g.Vnum; i++)//检查与此点相邻的点继续遍历
	{
		if (g.M[s][i] && !visit[i])
			DFS(g, i, visit);
	}
}

void Start(Graph g)
{
	int s,visit[20];
	cout << "input start point:";
	cin >> s;//输入起点下标
	memset(visit, 0, sizeof(visit));//将状态矩阵置0
	DFS(g, s, visit);
	for (int i = 0; i < g.Vnum; i++)//防止图不连通,那么需要看看还有没有其他连通分支
		DFS(g, i, visit);
}

int main()
{
	Graph g = CreateG();
	Start(g);
}

  例题3->城堡问题

#include <iostream>
#include<bitset>
using namespace std;

const int N = 10;
int room[N][N];//储存各个房间的靠墙情况
int color[N][N];//标记room[i][j]是否已经走过

int DFSRoom(int i, int j, int& area)
{
	if (color[i][j])//若此房间已经走过,返回
		return 0;
	area++;//否则发现新房间,面积+1
	color[i][j] = 1;//标记

	int temp = room[i][j];
	room[i][j] &= 1;//通过按位运算来判断此房间有哪些门
	if ((room[i][j] == 0) && j - 1 >= 0)
		DFSRoom(i, j - 1, area);
	room[i][j] = temp;
	room[i][j] &= 2;
	if ((room[i][j] == 0) && i - 1 >= 0)
		DFSRoom(i - 1, j, area);
	room[i][j] = temp;
	room[i][j] &= 4;
	if ((room[i][j] == 0) && j + 1 < N)
		DFSRoom(i, j + 1, area);
	room[i][j] = temp;
	room[i][j] &= 8;
	if ((room[i][j] == 0) && i + 1 < N)
		DFSRoom(i + 1, j, area);
	return area;
}

void Ready()
{
	int num = 0, max = 0;
	memset(color, 0, sizeof(color));
	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			cin >> room[i][j];

	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			if (!color[i][j])//发现新房间
			{
				num++;//房间数量+1
				int temp = 0;
				int& area = temp;
				max = (max, DFSRoom(i, j, area));
			}
		}
	}

	cout << "number of room:" << num << " and the biggest area of room is " << max << endl;
}

int main()
{
	Ready();
}

例题4->踩方格

#include <iostream>
using namespace std;

const int N = 5;
int map[N][N];
int visit[N][N];

int Ways(int x, int y, int n)
{
	if (n == 0)//n=0说明已经用完步数
		return 1;
	int num = 0;
	visit[x][y] = 1;

	if (!visit[x - 1][y] && x - 1 >= 0)//向北走
		num += Ways(x - 1, y, n - 1);
	if (!visit[x][y - 1] && y - 1 >= 0)//向西走
		num += Ways(x, y - 1, n - 1);
	if (!visit[x][y + 1] && x + 1 < N)//向东走
		num += Ways(x, y + 1, n - 1);
	visit[x][y] = 0;//此种情况结束后,把其标志重新置为0,防止干扰其他情况(非常关键的一步)
	return num;
}

int main()
{
    for(int i=0; i < N; i++)
        for(int j=0; j < N; j++)
            map[i][j] = 1;
	int visit[5][5];
	memset(visit, 0, sizeof(visit));
	int x, y, n;
	cout << "input x,y and n:";
	cin >> x >> y >> n;
	while(x >= N || y >= N || x < 0 || y < 0 || n > N * N)//防止非法输入
	{
		cout << "input invalid input, input x,y and n again:";
		cin >> x >> y >> n;
	}
	cout << "total " << Ways(x, y, n) << " ways" << endl;
}

例题5->寻路问题(剪枝思想)

#include <iostream>
#include<vector>
#include<limits>
using namespace std;

struct Road
{
	int Des, L, C;
};

vector<vector<Road>>road(100);//邻接表标识图的一种写法(很好用)
int totalfee, totalcity, totalroad;//解决问题需要的用户输入的最基础三个量
int totalcost = 0, minlen = numeric_limits<int>::max(), totallen = 0;//在递归中需要用到的三个量
int visit[100], minroad[100][100];//一个是查看是否访问过的标志矩阵,一个是储存着走到i城市且花了j钱所能走的最优路径

void Walk(int S)
{
	if (S == totalcity)//此时说明已经走到终点
	{
		minlen = min(minlen, totallen);//判断是否需要更新最优路径
		return;
	}
	for (int i = 0; i < road[i].size(); i++)//循环遍历与此起点相连的每个终点
	{
		Road r = road[S][i];//以S为起点i为终点的路
		if (totalcost + r.C > totalfee)//若走这条路花的钱超出预算则放弃这条路
			continue;
		if (!visit[r.Des])//若这条路在这种情况下还没有被走过
		{
			if (totallen + r.L >= minlen)//若走这条路后的总路径已经大于当前求得的最优路径,则放弃这条路(剪枝)
				continue;
			if (r.L + totallen >= minroad[r.Des][totalcost + r.C])//若选择这条路:走到e.Des这座城市且花了totalcos+r.C的钱的总路径已经大于有记载的此种情况下的总路径,那么放弃(剪枝)
				continue;
			minroad[r.Des][totalcost + r.C];//若上述if不符合,那么更新上述情况下的最优路径

			totallen += r.L;//更新路径
			totalcost += r.C;//更新花费
			visit[r.Des] = 1;//更新标识
			Walk(r.Des);//递归
			visit[r.Des] = 0;//为了不影响后续的遍历,将已经改变的量重新变为上一步的值
			totallen -= r.L;
			totalcost-=r.C;
		}
	}
}

void Ready()
{
	cout << "input totalcity, totalroad and totalfee:";
	cin >> totalcity >> totalroad >> totalfee;
	memset(visit, 0, sizeof(visit));

	for (int i = 0; i < totalroad; i++)
	{
		Road r;
		int s;
		cout << "input s,Des,L and C:";
		cin >> s >> r.Des >> r.L >> r.C;
		if (r.Des != i)//防止有路径的期待和终点相同
		{
			road[s].push_back(r);
		}
	}

	visit[0] = 1;
	Walk(0);
	if (minlen < numeric_limits<int>::max())//如果最终的minroad小于最开始预设的最大值,那么说明找到了最短路径
		cout << minlen << endl;
	else
		cout << "no such way" << endl;
}
int main()
{
	Ready();
}

例题6->生日蛋糕(剪枝思想)

#include <iostream>
#include<limits>
using namespace std;

int minarea = numeric_limits<int>::max(), area = 0;
int Floor, V, R, H;
int best[21][301];

void Cake(int floor, int v, int r, int h)
{
	if (floor == 0)//层数为0,已经放置蛋糕完毕
	{
		if (v)//若体积还不满足,返回重新查找
			return;
		else//体积也满足,看看是否需要更新最优值
			minarea = min(area, minarea);
	}

	if (v <= 0)//若体积小于0,返回重新查找
		return;

	for (int i = r; i >= floor; i--)//循环取合适的半径值
	{
		if (Floor == floor)//若这俩相等,说明现在是整个蛋糕的最底层,那么整个圆面积就是最大面积的平方
			area = pow(i, 2);
		for (int j = h; j >= floor; j--)//循环取合适的高值
		{
			if (v - pow(i, 2) * j < 0)//(剪枝)若取此种情况后体积超出下限,放弃这种情况
				continue;
			area += 2 * i * j;//面积更新
			if (area > best[floor][v - i * i  * j])//(剪枝)若取此种情况,得到的结果超过此种情况之前所记录过的最优解,放弃
				continue;
			else
				best[floor][v - i * i * j] = area;//若是此种情况的最优解,则更新
			Cake(floor - 1, v - pow(i, 2) * j, i - 1, j - 1);//递归继续查找
			area -= 2 * i * j;//将改变过的值改回,防止影响其他情况下的判断
		}
	}
}

int main()
{
	for (int i = 0; i <= 20; i++)
		for (int j = 0; j <= 300; j++)
			best[i][j] = 1e4;
	cout << "input Floor and V:";
	cin >> Floor >> V;
	Cake(Floor, V, 5, 5);
	if (minarea < numeric_limits<int>::max())
		cout << "minimum area is " << minarea << endl;
	else
		cout << "no suitable way to cut" << endl;
}

广度优先搜索

贪心算法

例题1->圣诞老人的礼物

#include <iostream>
#include<algorithm>
#define EPS 1e-6
using namespace std;

struct Gift
{
	int V;
	int W;
	bool operator <(const Gift& c)
	{
		return double(V / W) > double(c.V / c.W) > EPS;// 重载>运算符
	}
}gift[100];

void Pick()
{
	int n, weight = 0;
	cout << "input how many gifts and total weight:";
	cin >> n >> weight;

	for (int i = 0; i < n; i++)
	{
		cout << "input the value and weight of the gift:";
		cin >> gift[i].V >> gift[i].W;
	}
	sort(gift, gift + n);//依据价值/质量(比值)来进行排序
	double totalw = 0, totalv = 0;

	for (int i = 0; i < n; i++)
	{
		if (totalw += gift[i].W <= weight)//还能放得下这个礼物
		{
			totalw += gift[i].W;
			totalv += gift[i].V;
		}
		else//放不下整个这个礼物了,那么放一部分进去
		{
			totalv += gift[i].V / gift[i].W * (weight - totalw);
			break;//可以结束放礼物了
		}
	}

	cout << "max value is " << totalv << endl;
}

int main()
{
	Pick();
}

例题2->电影节

#include <iostream>
#include<algorithm>
using namespace std;

struct Film
{
	int S;
	int E ;
	bool operator <(const Film& f)
	{
		return E < f.E;// 重载<运算符
	}
}film[100];

void Watch()
{
	int n, flag = 0;
	cout << "input how many movies:";
	cin >> n;

	for (int i = 0; i < n; i++)
	{
		cout << "input the start time and end time of the movie:";
		cin >> film[i].S >> film[i].E;
	}
	sort(film, film + n);//依据电影结束时间来进行排序
	int totalm = 1;//预设为1,至少看一场电影

	for (int i = 1; i < n; i++)//第一场电影肯定会看,所以从第二场电影开始计数
	{
		if (film[i].S >= film[flag].E && film[i].E < 24)//下一场的开始时间在这一场的结束时间之后且不超过晚上24点
		{
			totalm++;
			flag = i;
		}
	}

	cout << "max value is " << totalm << endl;
}

int main()
{
	Watch();
}

例题3->分配畜栏(待补)

例题4->放置雷达(待补)

例题5->钓鱼(待补)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值