例题代码|程序设计与算法(二) 算法基础 北大 郭炜 中国大学MOOC 笔记

  • 网站链接
    b站算法基础 国家精品 北京大学 郭炜-哔哩哔哩
    中国大学慕课程序设计与算法(二)算法基础 北京大学 郭炜

代码均由个人整理,有错误欢迎指正!

中国大学MOOC北大郭炜 算法基础

  • 枚举
    • 完美立方
    • 生理周期
    • 称硬币
    • 熄灯问题
  • 递归
    • 汉诺塔
    • N皇后
    • 逆波兰表达式
    • 表达式求值
    • 上台阶
    • 放苹果
    • 算24
  • 二分
    • BinarySearch模板
    • LowerBound模板
    • 单增方程求解
    • 找一对数
    • 农夫和奶牛
  • 分治
    • 归并排序模板
    • 快速排序模板
    • 输出前m大的数
    • 求排列的逆序数
  • 动态规划
    • 数字三角形
      • 记忆递归
      • 一维递推
    • 最长上升子序列
    • 最长公共子序列
    • 最佳加法表达式
    • Help Jimmy
    • 滑雪
      • 我为人人型
      • 人人为我型
    • 神奇的口袋
    • 0-1背包问题
    • 分蛋糕
  • 深度优先
    • 城堡问题
    • 踩方格
    • 寻路问题
    • 生日蛋糕
  • 广度优先
    • 抓住这头牛
    • 迷宫问题
    • 八数码问题
  • 贪心
    • 圣诞老人的礼物
    • 电影节
    • Stall Reservations
    • Radar Installation
    • 钓鱼

枚举

一个个试

完美立方

#include
using namespace std;
int main()
{
	int N = 100;
	for (int a = 2; a < N; a++)
		for (int b = 2; b < a - 1; b++)
			for (int c = 2; c < b - 1; c++)
				for(int d=2;d<c-1;d++)
					if(a*a*a==b*b*b+c*c*c+d*d*d)
						cout<< "Cube=" << a << "," << "Triple=" << "(" << b << "," << c << "," << d << "," << ")" << endl;
	return 0;
}

生理周期

#include
using namespace std;
int main()
{
	int p, e, i, d;
	int c = 0;
	while (cin >> p >> e >> i >> d && p!=-1)
	{
		c++;
		int k;
		for (k = d + 1; (k - p) % 23; k++);
		for (; (k - e) % 28; k += 23);
		for (; (k - i) % 33; k += 23 * 28);
		cout << "Case " << c << ": the next triple peak occurs in " << k - d << " days" << endl;
	}
	return 0;
}

称硬币

#include
#include
using namespace std;
char Left[3][7];
char Right[3][7];
char result[3][7];
bool IsFake(char c, bool light);
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		for (int i = 0; i < 3; i++)
			cin >> Left[i] >> Right[i] >> result[i];
		for (char c = 'A'; c <= 'L'; c++)
		{
			if (IsFake(c, true))
			{
				cout << c << "is the counterfeit coin and it is light" << endl;
				break;
			}
			else if (IsFake(c, false))
			{
				cout << c << "is the counterfeit coin and it is heavy" << endl;
				break;
			}
		}
	}
	return 0;
}
bool IsFake(char c, bool light)
{
	for (int i = 0; i < 3; i++)
	{
		char* pLeft, * pRight;
		if (light)
		{
			pLeft = Left[i];
			pRight = Right[i];
		}
		else
		{
			pLeft = Right[i];
			pRight = Left[i];
		}
		switch (result[i][0]) {
		case'u':
			if (strchr(pRight, c) == NULL)return false;
			break;
		case'e':
			if (strchr(pLeft, c) || strchr(pRight, c)); return false;
			break;
		case'd':
			if (strchr(pLeft, c) == NULL)return false;
			break;
		}
	}
	return true;
}

熄灯问题

局部思想
二进制枚举

#include
#include
#include
#include
using namespace std;
char oriLights[5];
char lights[5];
char result[5];

int GetBit(char c, int i)
{
	return (c >> i) & 1;//右移
}
void SetBit(char &c, int i, int v)//其它位不变,第i位改成v
{
	if (v)
		c |= (1 << i);
	else
		c &= ~(1 << i);//只有i位1,其它0;再反;再与c与,c就是第i位为0其它不变
}
void FlipBit(char& c, int i)//翻转
{
	c ^= (1 << i);//c的第i位翻转
}
void OutputResult(int t, char result[])
{
	cout << "PUZZLE #" << t << endl;
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 6; j++)
		{
			cout << GetBit(result[i], j);
			if (j < 5)cout << ' ';
		}
		cout << endl;
	}
}
int main()
{
	int T; cin >> T;
	for(int t=1;t<=T;t++)
	{
		for (int i = 0; i < 5; i++)
		{
			for (int j = 0; j < 6; j++)
			{
				int s; cin >> s;
				SetBit(oriLights[i], j, s);
			}
		}
			
		for (int n = 0; n < 64; n++)
		{
			int switchs = n;
			memcpy(lights, oriLights, sizeof(oriLights));
			for (int i = 0; i < 5; i++)
			{
				result[i] = switchs;
				for (int j = 0; j < 6; j++)
				{
					if (GetBit(switchs, j))//是1才需要处理
					{
						if (j > 0)
							FlipBit(lights[i], j-1);
						FlipBit(lights[i], j);
						if (j < 5)
							FlipBit(lights[i], j + 1);
					}
				}
				if (i < 4)
					lights[i + 1] ^= switchs;//翻转下一行
				switchs = lights[i];//确定下一行开关状态
				
			}
			if (lights[4] == 0)
			{
				OutputResult(t, result);
				break;
			}
		}
	}
	return 0;
}

递归

函数调用其自身

汉诺塔

#include
using namespace std;
void Hanoi(int n, char src, char mid, char dest)//src上n个以mid为中转移到dest
{
	if (n == 1)
	{
		cout << src << "->" << dest << endl;
		return;
	}
	Hanoi(n - 1, src, dest, mid);//将n-1从src到mid
	cout << src << "->" << dest << endl;//将1个从src到dest
	Hanoi(n - 1, mid, src, dest);//将n-1从mid到dest
	return;
}
int main()
{
	int n;
	cin >> n;
	Hanoi(n, 'A', 'B', 'C');
	return 0;
}

N皇后

用递归替代多重循环
可以输出所有结果

#include
#include
using namespace std;
int N;
int queenPos[100];
void NQueen(int k);
int main()
{
	cin >> N;
	NQueen(0);//从第0行开始摆皇后
	return 0;
}
void NQueen(int k)//前k-1行已经摆好
{
	int i;
	if (k == N)
	{
		for (i = 0; i < N; i++)
			cout << queenPos[i] + 1 << ' ';
		cout << endl;
		return;
	}
	for (i = 0; i < N; i++)//尝试第k行皇后的位置
	{
		int j;
		for (j = 0; j < k; j++)//与前面的皇后比较位置看是否有重复
		{
			if (queenPos[j] == i || abs(queenPos[j] - i) == abs(k - j))
				break;
		}
		if (j == k)
		{
			queenPos[k] = i;
			NQueen(k + 1);
		}
	}
}

逆波兰表达式

用递归解决递归形式的问题

#include
using namespace std;
double exp()
{
	char s[20];
	cin >> s;
	switch (s[0]) 
	{
	case'+':return exp() + exp();
	case'-':return exp() - exp();
	case'*':return exp() * exp();
	case'/':return exp() / exp();
	default:return atof(s);//字符串转浮点数
		break;
	}
}
int main()
{
	cout << exp() << endl;;
	return 0;
}

表达式求值

#include
using namespace std;
int factor_value();
int term_value();
int expression_value();
int main()
{
	cout << expression_value() << endl;
	return 0;
}

int factor_value()
{
	int result = 0;
	char c = cin.peek();
	if (c == '(')
	{
		cin.get();
		result = expression_value();
		cin.get();
	}
	else
	{
		while (isdigit(c))//如果是数字,不知道是几位
		{
			result = 10 * result + c - '0';
			cin.get();
			c = cin.peek();
		}
	}
	return result;
}
int term_value()
{
	int result = factor_value();//求第一个因子
	while (true)
	{
		char op = cin.peek();//读取一个字符
		if (op == '*' || op == '/')
		{
			cin.get();//取走一个字符
			int value = factor_value();
			if (op == '*')
				result *= value;
			else
				result /= value;
		}
		else
			break;
	}
	return result;
}
int expression_value()
{
	int result = term_value();//求第一项的值
	bool more = true;
	while (more)
	{
		char op = cin.peek();//读取一个字符
		if (op == '+' || op == '-')
		{
			cin.get();//取走一个字符
			int value = term_value();
			if (op == '+')
				result += value;
			else
				result -= value;
		}
		else
			more = false;
	}
	return result;
}

上台阶

#include
using namespace std;
int N;
int stairs(int n)
{
	if (n == 1)return 1;
	if (n == 2)return 2;
	return stairs(n - 1) + stairs(n - 2);
}
int main()
{
	while (cin >> N)
	{
		cout << stairs(N) << endl;
	}
	return 0;
}

放苹果

#include
using namespace std;
int f(int m, int n)
{
	if (n > m)return f(m, m);
	if (m == 0)return 1;//没有苹果是1种放法
	if (n == 0)return 0;//没有盘子是0种放法
	return f(m, n - 1) + f(m - n, n);//有盘子为空的+没有盘子为空的
}
int main()
{
	int t, m, n;
	cin >> t;
	while (t--)
	{
		cin >> m >> n;
		cout << f(m, n) << endl;
	}
	return 0;
}

算24

枚举+递归

#include
using namespace std;
double a[5];
#define eps 1e-6
bool isZero(double x)
{
	return fabs(x) <= eps;
}
bool count24(double a[], int n)
{
	if (n == 1)
	{
		if (isZero(a[0] - 24))return true;
		else return false;
	}
	double b[5];
	for (int i = 0; i < n - 1; i++)
		for (int j = i + 1; j < n; j++)//枚举两数组合
		{
			int m = 0;//剩下的数个数m,m=n-2
			for (int k = 0; k < n; k++)//然后把剩下的数放进数组b
				if (k != i && k != j)b[m++] = a[k];
			b[m] = a[i] + a[j];
			if (count24(b, m + 1))return true;//m+1=n-1
			b[m] = a[i] - a[j];
			if (count24(b, m + 1))return true;
			b[m] = a[j] - a[i];
			if (count24(b, m + 1))return true;
			b[m] = a[i] * a[j];
			if (count24(b, m + 1))return true;
			if (!isZero(a[j]))
			{
				b[m] = a[i] / a[j];
				if (count24(b, m + 1))return true;
			}
			if (!isZero(a[i]))
			{
				b[m] = a[j] / a[i];
				if (count24(b, m + 1))return true;
			}
		}
	return false;
}
int main()
{
	for (int i = 0; i < 4; i++)
	{
		cin >> a[i];
	}
	if (count24(a, 4))cout << "YES" << endl;
	else cout << "NO" << endl;
	return 0;
}

二分

BinarySearch模板

int BinarySearch(int a[], int size, int p)
{
	int L = 0;
	int R = size - 1;
	while (L <= R)
	{
		int mid = L + (R - L) / 2;
		if (p == a[mid])
			return mid;
		else if (p > a[mid])
			L = mid + 1;
		else
			R = mid - 1;
	}
	return -1;
}

LowerBound模板

int LowerBound(int a[], int size, int p)
{
	int L = 0;
	int R = size - 1;
	int lastPos = -1;
	while (L <= R)
	{
		int mid = L + (R - L) / 2;
		if (a[mid]>=p)
			R = mid - 1;
		else
		{
			lastPos = mid;
			L = mid + 1;
		}		
	}
	return lastPos;
}

单增方程求解

#include
using namespace std;
double eps = 1e-6;
double f(double x)
{
	return x * x * x - 5 * x * x + 10 * x - 80;
}
int main()
{
	double root, x1 = 0, x2 = 100, y;
	root = x1 + (x2 - x1) / 2;
	int triedTimes = 1;
	y = f(root);
	while (fabs(y) > eps)
	{
		if (y > 0)x2 = root;
		else x1 = root;
		root = x1 + (x2 - x1) / 2;
		y = f(root);
		triedTimes++;
	}
	cout << root << endl;
	cout << triedTimes << endl;
	return 0;
}

找一对数

非二分但比二分快的方法3

#include
#include
using namespace std;
int main()
{
	int n,m; cin >> n>>m;
	int a[100000];
	for (int i = 0; i < n; i++)
		cin >> a[i];
	sort(a, a + n);
	int i = 0, j = n - 1;
	while (i < j)
	{
		if (a[i] + a[j] == m)
		{
			cout << a[i] << ' ' << a[j] << endl;
			break;
		}
		else if (a[i] + a[j] > m)j--;
		else i++;
	}
	if (i == j)cout << "no ans" << endl;
	return 0;
}

农夫和奶牛

难点在怎么判断距离对不对

#include
#include
using namespace std;
int a[100005];
int n, c;
bool judge(int dis) {
	int num = 1, pre = a[0];
	for (int i = 0; i < n; i++) {
		if (a[i] - pre >= dis) {
			num++;
			pre = a[i];
			if (num == c) {
				return true;
			}
		}
	}
	return false;
}
int main() {
	cin>>n>>c;
	for (int i = 0; i < n; i++) {
		cin>>a[i];
	}
	sort(a, a + n);
	int l = 0, r = 1000000000 / c;//答案在lr范围内
	int mid;
	int ans;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (judge(mid)) {
			ans = mid;
			l = mid + 1;
		}
		else {
			r = mid - 1;
		}
	}
	cout << ans << endl;
	return 0;
}

分治

归并排序模板

空间O(n) 需要一个同样大小的额外空间

#include
using namespace std;
int a[10] = { 13, 27, 19, 2, 8, 12, 2, 8, 30, 89 };
int b[10];
void Merge(int a[], int s, int m, int e, int tmp[])//s--m,m+1--e合并到tmp,且保证tmp有序,再拷回a
{
	int pb = 0;
	int p1 = s, p2 = m + 1;
	while (p1 <= m && p2 <= e)
	{
		if (a[p1] < a[p2])tmp[pb++] = a[p1++];
		else tmp[pb++] = a[p2++];
	}
	while (p1 <= m)	tmp[pb++] = a[p1++];
	while (p2 <= e)	tmp[pb++] = a[p2++];
	for (int i = 0; i <= e - s ; i++)a[s + i] = tmp[i];
}
void MergeSort(int a[],int s,int e,int tmp[])//数组,开始结尾下标,中转
{
	if (s < e)
	{
		int m = s + (e - s) / 2;
		MergeSort(a, s, m, tmp);
		MergeSort(a, m + 1, e, tmp);
		Merge(a, s, m, e, tmp);
	}
}
int main()
{
	int size = sizeof(a) / sizeof(int);
	MergeSort(a, 0, size - 1, b);
	for (int i = 0; i < size; i++)
		cout << a[i] << ' ';
	cout << endl;
	return 0;
}

快速排序模板

#include
using namespace std;
int a[] = { 93,27,30,2,8,12,2,8,30,89 };
void swap(int& a, int& b)
{
	int tmp = a; a = b; b = tmp;
}
void QuickSort(int a[], int s, int e)
{
	if (s >= e)return;
	int k = a[s];
	int i = s, j = e;
	while (i < j)
	{
		while (j > i && a[j] >= k)j--;
		swap(a[i], a[j]);
		while (i < j && a[i] < k)i++;
		swap(a[i], a[j]);
	}
	QuickSort(a, s, i - 1);
	QuickSort(a, i + 1, e);
}
int main()
{
	int size = sizeof(a) / sizeof(int);
	QuickSort(a, 0, size - 1);
	for (int i = 0; i < size; i++)
		cout << a[i] << ' ';
	cout << endl;
	return 0;
}

输出前m大的数

分治+快排

#include
#include
using namespace std;
int  a[1000005]; //当数据过大时要放在函数外面防止栈溢出
int  n,m;
void  QuickSort(int  low, int  high) {
    int  tmp;
    tmp = a[low];
    int  l = low;
    int  r = high;
    if (l > r)
        return;
    while (l != r) {
        while (a[r] < tmp && l < r) r--;
        if (l < r) 
        {
            a[l] = a[r];
            l++;
        }
        while (a[l] > tmp && l < r) l++;
        if (l < r) {
            a[r] = a[l];
            r--;
        }
    }
    a[l] = tmp;
    if (m <= l + 1) QuickSort(low, l - 1);
    else {
        QuickSort(low, l - 1);
        QuickSort(l + 1, high);
    }
}
int  main() {
    while (cin>>n>>m) {
        for (int i = 0; i < n; i++)
            cin >> a[i];
        QuickSort(0, n - 1);
        for (int i = 0; i < m; i++) {
            cout << a[i];
            if (i != m - 1)
                cout<<' ';
        }
        cout << endl;
    }
    return  0;
}

求排列的逆序数

归并
劈一半,左边数+右边数+左边比右边大的数

#include
using namespace std;
int a[100005], tmp[100005];
long long sum;
void MergeSort(int l, int m, int r)
{
    int p1 = l, p2 = m + 1;
    int pt = l;
    while (p1 <= m && p2 <= r)
    {
        if (a[p1] <= a[p2])tmp[pt++] = a[p1++];
        else
        {
            sum += m - p1 + 1;//逆序总数 
            tmp[pt++] = a[p2++];
        }
    }
    while (p1 <= m) tmp[pt++] = a[p1++]; //将剩下的直接放入 
    while (p2 <= r) tmp[pt++] = a[p2++]; 
    for (int i = l; i <= r; i++)
        a[i] = tmp[i];//将排好的数放回 
}
void Merge_sort(int l, int r)
{
    if (l < r)
    {
        int mid = (l + r) >> 1;
        Merge_sort(l, mid);//归并左边 
        Merge_sort(mid + 1, r);//归并右边 
        MergeSort(l, mid, r);//归并排序 
    }
}
int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    Merge_sort(0, n-1);
    cout << sum << endl;//输出逆序总数 
    return 0;
}

动态规划

递归转换成递推

方式 n 范围 ans 优点 缺点
递归 个参数 参数取值 递归函数返回值 直观易写 爆栈可能性高
递推 维数组 数组下标 数组元素的值 效率高 滚动数组节省空间 逆向思维
  • 递推:从边界值开始逐步填充数组,相当于计算递归的逆过程
    状态 值 状态转移方程
  • 动规特点:问题具有最优子结构;无后效性

数字三角形

记忆递归

记忆递归型动规程序 防重复计算超时

#include
#include
using namespace std;
int D[101][101];
int n;
int maxSum[101][101];
int MaxSum(int i,int j)
{
	if (maxSum[i][j] != -1)return maxSum[i][j];
	if (i == n) maxSum[i][j] = D[i][j];
	else
	{
		int x = MaxSum(i + 1, j);
		int y = MaxSum(i + 1, j + 1);
		maxSum[i][j] = max(x, y) + D[i][j];
	}
	return maxSum[i][j];
}
int main()
{
	cin >> n;
	for(int i=1;i<=n;i++)
		for (int j = 1; j <= i; j++)
		{
			cin >> D[i][j];
			maxSum[i][j] = -1;
		}
	cout << MaxSum(1, 1) << endl;
	return 0;
}

一维递推

空间优化:二维->一维,无需额外空间

#include
#include
using namespace std;
int D[101][101];
int n;
int* maxSum;//设指针
int main()
{
	cin >> n;
	for(int i=1;i<=n;i++)
		for (int j = 1; j <= i; j++)
			cin >> D[i][j];
	maxSum = D[n];//maxSum指向D的最后一行
	for (int i = n - 1; i >= 1; i--)
		for (int j = 1; j <= i; j++)
			maxSum[j] = max(maxSum[j], maxSum[j + 1]) + D[i][j];//maxSum[j]对应D[n][j]
	cout << maxSum[1]<< endl;//D最后一行一直在改动为和
	return 0;
}

最长上升子序列

#include
#include
#include
using namespace std;
int a[1010];
int maxLen[1010];
int main()
{
	int n; cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		maxLen[i] = 1;
	}
	for (int i = 0; i <= n; i++)//第i个数为终点的最长上升子序列长度
		for (int j = 1; j < i; j++)//看i左边的第j个数为终点的最长上升子序列
			if (a[i] > a[j])
				maxLen[i] = max(maxLen[i], maxLen[j] + 1);
	cout << *max_element(maxLen + 1, maxLen + n + 1) << endl;
	return 0;
}

最长公共子序列

#include
#include
using namespace std;
char s1[1000], s2[1000];
int maxLen[1000][1000];
int main()
{
	while (cin >> s1 >> s2)
	{
		int len1 = strlen(s1);
		int len2 = strlen(s2);
		for (int i = 0; i <= len1; i++)maxLen[i][0] = 0;
		for (int j = 0; j <= len2; j++)maxLen[0][j] = 0;
		for (int i = 1; i <= len1; i++)
			for (int j = 1; j <= len2; j++)
			{
				if (s1[i - 1] == s2[j - 1])
					maxLen[i][j] = maxLen[i - 1][j - 1] + 1;
				else
					maxLen[i][j] = max(maxLen[i][j - 1], maxLen[i - 1][j]);
			}
		cout << maxLen[len1][len2] << endl;
	}
	return 0;
}

最佳加法表达式

#include
using namespace std;
int a[1005], num[1005][1005], dp[1005][1005];//存数字串 i-j间的数 在i个数中插j个加号的表达式最小值
int main() {
	int n, m;
	while (cin>>n>>m)
	{
		for (int i = 1; i <= n; i++)
			cin >> a[i];
		for (int i = 1; i <= n; i++)//预处理,计算i到j数字串组成的数字 
			for (int j = i ; j <= n; j++) 
				num[i][j] = num[i][j - 1] * 10 + a[j];
		memset(dp, 0x3f, sizeof(dp));
		for (int i = 1; i <= n; i++)
			dp[0][i] = num[1][i];//无加号
		for (int i = 1; i <= m; i++)
			for (int j = i; j <= n; j++)
				for (int k = i; k <= j; k++)
					dp[i][j] = min(dp[i][j], dp[i - 1][k] + num[k + 1][j]);
					//对比递归t = min(t, V(m-1,i)+num[i+1][n]);
		cout << dp[m][n] << endl;
	}
}

Help Jimmy

#include
using namespace std;
int t, N, X, Y, MAX;
int x1[1010], x2[1010], h[1010], LeftMinTime[1010], RightMinTime[1010];
void h_sort(int x1[], int x2[], int h[]) {
	for (int i = 0; i < N; i++) {
		for (int j = i + 1; j < N; j++) {
			if (h[j] > h[i]) {
				swap(h[i], h[j]);
				swap(x1[i], x1[j]);
				swap(x2[i], x2[j]);
			}
		}
	}
}
int find(int x, int y) {
	for (int i = 0; i < N; i++)
		if (x >= x1[i] && x <= x2[i])
			if (y > h[i] && h[i] != 0)
				return i;
	return -1;
}
void FindLeft(int i, int l)
{
	if (l == -1 && h[i] <= MAX)
		LeftMinTime[i] = h[i];
	else if (h[i] - h[l] <= MAX)
		LeftMinTime[i] = h[i] - h[l] + min(LeftMinTime[l] + x1[i] - x1[l], RightMinTime[l] + x2[l] - x1[i]);
	else
		LeftMinTime[i] = 1000000000;
}
void FindRight(int i, int r)
{
	if (r == -1 && h[i] <= MAX)//板子i右端正下方没有别的板子,且高度离平面不超过max
		RightMinTime[i] = h[i];
	else if (h[i] - h[r] <= MAX)//板子i右端正下方的板子编号r,且俩块板子的高度差小于MAX
		RightMinTime[i] = h[i] - h[r] + min(LeftMinTime[r] + x2[i] - x1[r], RightMinTime[r] + x2[r] - x2[i]);
	else
		RightMinTime[i] = 1000000000;//高度超了
}
int main() {
	cin >> t;
	while (t--)
	{
		cin >> N >> X >> Y >> MAX;
		for (int i = 0; i < N; i++)
			cin >> x1[i] >> x2[i] >> h[i];
		h_sort(x1, x2, h);//排序
		int firstB = find(X, Y);//找第一块板子 
		if (firstB == -1) {//没有答案
			cout << Y << endl;
			continue;
		}
		for (int i = N - 1; i >= 0; i--) {
			FindLeft(i, find(x1[i], h[i]));
			FindRight(i, find(x2[i], h[i]));
		}
		int minTime = Y - h[firstB] + min(LeftMinTime[firstB] + X - x1[firstB], RightMinTime[firstB] + x2[firstB] - X);
		cout << minTime << endl;
	}
	return 0;
}

滑雪

我为人人型

#include
#include
using namespace std;
int R, C;
int h[105][105];
int L[105][105];
int to[4][2] = { 1,0,-1,0,0,1,0,-1 };
int ans = 1;
struct node {
	int x;
	int y;
	int high;
};
bool cmp(node a, node b) 
{
	return a.high < b.high;
}
void maxL(node s) 
{
	int x = s.x, y = s.y;
	for (int i = 0; i < 4; i++)
	{
		int x2 = x + to[i][0], y2 = y + to[i][1];//遍历周围点
		if (x2 > 0 && y2 > 0 && x2 <= R && y2 <= C) 
		{
			if (L[x2][y2] < L[x][y] + 1 && h[x2][y2] > h[x][y])//长度比原来点小且高度比原来高
			{
				L[x2][y2] = L[x][y] + 1;
				if (ans < L[x2][y2])
					ans = L[x2][y2];
			}
		}
	}
}
int main() {
	cin >> R >> C;
	int k = 0;
	node s[10005];
	for (int i = 1; i <= R; i++) 
	{
		for (int j = 1; j <= C; j++) 
		{
			cin >> h[i][j];
			k++;
			s[k].high = h[i][j];
			s[k].x = i;
			s[k].y = j;
			L[i][j] = 1;
		}
	}
	sort(s + 1, s + k + 1, cmp);
	for (int i = 1; i <= k; i++) {
		maxL(s[i]);
	}
	cout << ans << endl;
	return 0;
}

人人为我型

#include
#include
using namespace std;
int R, C;
int h[105][105];
int L[105][105];
int to[4][2] = { 1,0,-1,0,0,1,0,-1 };
int ans = 1;
struct node {
	int x;
	int y;
	int high;
};
bool cmp(node a, node b) 
{
	return a.high < b.high;
}
void maxL(node s) 
{
	int x = s.x, y = s.y;
	for (int i = 0; i < 4; i++) {
		int x2 = x + to[i][0], y2 = y + to[i][1];
		if (x2 > 0 && y2 > 0 && x2 <= R && y2 <= C) 
		{
			if (h[x][y] > h[x2][y2]) 
			{
				L[x][y] = max(L[x2][y2] + 1, L[x][y]);
				ans = max(ans, L[x][y]);
			}
		}
	}
}
int main() 
{
	cin >> R >> C;
	int k = 0;
	node s[10005];
	for (int i = 1; i <= R; i++) 
	{
		for (int j = 1; j <= C; j++) 
		{
			cin >> h[i][j];
			k++;
			s[k].high = h[i][j];
			s[k].x = i;
			s[k].y = j;
			L[i][j] = 1;
		}
	}
	sort(s + 1, s + k + 1, cmp);
	for (int i = 1; i <= k; i++) {
		maxL(s[i]);
	}
	cout << ans << endl;
	return 0;
}

神奇的口袋

先写递归思路再逆推递推方法会简单一些

#include
using namespace std;
int a[30], n, ways[40][30];
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		ways[0][i] = 1;
	}
	ways[0][0] = 1;
	for(int w=1;w<=40;w++)
		for (int k = 1; k <= n; k++)
		{
			ways[w][k] = ways[w][k - 1];
			if (w - a[k] >= 0)
				ways[w][k] += ways[w - a[k]][k - 1];
				//对比递归方法:w==1re1;k<=0re0; 
				//return ways(w, k - 1) + ways(w - a[k], k - 1);
		}
	cout << ways[40][n] << endl;
	return 0;
}

0-1背包问题

二维空间太大&下一行的值只用到了上一行的俩值->压缩成滚动数组,从上到下,从右到左求

#include
using namespace std;
int n, m, w[3505], d[3505];
int f[13000];//取前i种物品,总体积不超过j的最优价值总和
int main()
{
	cin >> m >> n;
	for (int i = 1; i <= n; i++)
		cin >> w[i] >> d[i];
	for (int i = 1; i <= n; i++)
		for (int j = m; j >= w[i]; j--)
			f[j] = max(f[j], f[j - w[i]] + d[i]);
//二维方法
/*for (int i = 1; i <= n; i++)
		for (int j = m; j >= w[i]; j--)
			f[i][j] = max(f[i-1][j], f[i-1][j - w[i]] + d[i]);*/
	cout << f[m] << endl;
	return 0;
}

分蛋糕

#include 
using namespace std;
const int INF = 0x3f3f3f3f;
int minS[21][21][21];//将i*j切k刀,最大面积下限
int main()
{
    int w, h, m;
    while (cin >> w >> h >> m) 
    {
        if (w == 0)break;
        memset(minS, INF, sizeof(minS));
        for (int i = 1; i <= w; i++) {
            for (int j = 1; j <= h; j++) {
                for (int k = 0; k <= m - 1; k++)
                {
                    if (k == 0)
                        minS[i][j][k] = i * j;//当一块蛋糕不分时
                    else if (i * j < k + 1)
                        minS[i][j][k] = INF;//失败设为无穷大
                    else
                    {
                        for (int r = 1; r < i; r++)// 横着切
                            for (int q = 0; q < k; q++)
                                minS[i][j][k] = min(minS[i][j][k], max(minS[r][j][q], minS[i - r][j][k - q - 1]));
                                //不切或者第一刀的最好结果
                        for (int c = 1; c < j; c++)// 竖着切
                            for (int q = 0; q < k; q++)
                                minS[i][j][k] = min(minS[i][j][k], max(minS[i][c][q], minS[i][j - c][k - q - 1]));
                    }
                }
            }
        }
        cout << minS[w][h][m - 1] << endl;
    }
}

深度优先

城堡问题

#include
using namespace std;
int R, C;
int rooms[60][60];
int color[60][60];
int maxRoomArea = 0, roomNum = 0;
int roomArea;//当前房间面积
void Dfs(int i, int j)//从(i,j)开始探索房间
{
	if (color[i][j])return;
	roomArea++;
	color[i][j] = roomNum;
	if ((rooms[i][j] & 1) == 0)Dfs(i, j - 1);//第一位为0说明没有西墙
	if ((rooms[i][j] & 2) == 0)Dfs(i - 1, j);
	if ((rooms[i][j] & 4) == 0)Dfs(i, j + 1);
	if ((rooms[i][j] & 8) == 0)Dfs(i + 1, j);
}
int main()
{
	cin >> R >> C;
	for (int i = 1; i <= R; i++)
		for (int j = 1; j <= C; j++)
			cin >> rooms[i][j];
	memset(color, 0, sizeof(color));
	for(int i=1;i<=R;i++)
		for (int j = 1; j <= C; j++)
		{
			if (!color[i][j])//找到了新房间
			{
				roomNum++;
				roomArea = 0;
				Dfs(i, j);
				maxRoomArea = max(roomArea, maxRoomArea);
			}
		}
	cout << roomNum << endl;
	cout << maxRoomArea << endl;
	return 0;
}

踩方格

#include
using namespace std;
int visited[30][50];
int ways(int i, int j, int n)//ij出发走n步的方案数
{
	if (n == 0)
		return 1;
	visited[i][j] = 1;
	int num = 0;
	if (!visited[i][j - 1])num += ways(i, j - 1, n - 1);
	if (!visited[i][j + 1])num += ways(i, j + 1, n - 1);
	if (!visited[i + 1][j])num += ways(i + 1, j, n - 1);
	visited[i][j] = 0;//ij对别的点是没走过的,要清0
	return num;
}
int main()
{
	int n;
	cin >> n;
	memset(visited, 0, sizeof(visited));
	cout << ways(0, 25, n) << endl;
	return 0;
}

寻路问题

剪枝

#include
#include
using namespace std;
int K, N, R;
struct Road
{
	int d, L, t;//终点,长度,价格
};
vector<vector<Road>>G(110);//i是起点
int minLen=1<<30;//目前最佳路径长度
int totalLen;//正在探索的路径长度
int totalCost;//正在探索的路已经花了多少钱
int visited[110];
int minL[110][100010];//1到城市i花了j的情况下的minLen
void dfs(int s)
{
	if (s == N)//走到终点
	{
		minLen = min(totalLen, minLen);
		return;
	}
	for (int i = 0; i < G[s].size(); i++)
	{
		Road r = G[s][i];
		if (totalCost + r.t > K)
			continue;
		if (!visited[r.d])
		{
			if (totalLen + r.L >= minLen)
				continue;//剪枝,已经超过min就直接试下一种方法
			if (totalLen + r.L >= minL[r.d][totalCost + r.t])
				continue;//记录中间结果的剪枝
			else
				minL[r.d][totalCost + r.t] = totalLen + r.L;
			totalLen += r.L;
			totalCost += r.t;
			visited[r.d] = 1;
			dfs(r.d);
			visited[r.d] = 0;
			totalLen -= r.L;
			totalCost -= r.t;
		}
	}
}
int main()
{
	cin >> K >> N >> R;
	for (int i = 0; i < R; i++)
	{
		int s;
		Road r;
		cin >> s >> r.d >> r.L >> r.t;
		if (s != r.d)
		{
			G[s].push_back(r);
		}
	}
	memset(visited, 0, sizeof(visited));
	visited[1] = 1;
	for (int i = 0; i < 110; i++)
		for (int j = 0; j < 10010; j++)
			minL[i][j] = 1 << 30;
	dfs(1);//从1开始搜索
	if (minLen < (1 << 30))
		cout << minLen << endl;
	else
		cout << -1 << endl;
	return 0;
}

生日蛋糕

dfs本质还是枚举
剪枝分为最优性剪枝和可行性剪枝

#include 
using namespace std;
int area, N, M, minArea;
void dfs(int v, int n, int r, int h)//n层凑体积v,半径<=r,高<=h
{
    if (n == 0)
    {
        if (v == 0) //层数和体积都满足条件
            minArea = min(minArea, area);
        return; //不管体积满不满足,层数到了就不能继续递归
    }
    if (area + 2 * v / r > minArea)
        return;  //剪枝1:area+剩余表面积的最小值 超过最优解
    for (int R = r; R >= n; R--)//剪枝2:半径和高度至少等于层数
        for (int H = h; H >= n; H--)
        {
            if (v < R * R * H)
                continue;//剪枝3:剩余的体积最大到不了还缺的体积
            if (n == M)
                area = R * R + 2 * R * H;     //如果是最底层
            else
                area += 2 * R * H;
            dfs(v - R * R * H, n - 1, R - 1, H - 1);
            area -= 2 * R * H;
        }
}
int main()
{
    cin >> N >> M;
    minArea = 1 << 30;
    dfs(N, M, sqrt(N) + 1, N + 1);  //因为v=r*r*h,所以半径最大为根号N,同理高最大为N,为避免出现不可知的错误所以+1
    if (minArea == 1 << 30)
        cout << 0 << endl;
    else
        cout << minArea << endl;
    return 0;
}

广度优先

抓住这头牛

#include
#include
using namespace std;
int N, K;
const int MAXN = 100000;
int visited[MAXN + 10];
struct Step {
	int x;//位置
	int steps;//起点到x需要的步数
	Step(int xx, int s) :x(xx), steps(s) {}
};
queue<Step>q;
int main()
{
	cin >> N >> K;
	memset(visited, 0, sizeof(visited));
	q.push(Step(N, 0));//放农夫起始位置
	visited[N] = 1;
	while (!q.empty())
	{
		Step s = q.front();
		if (s.x == K)//找到目标
		{
			cout << s.steps << endl;
			return 0;
		}
		if (s.x - 1 >= 0 && !visited[s.x - 1])//左边有位置且没走过
		{
			q.push(Step(s.x - 1, s.steps + 1));
			visited[s.x - 1] = 1;
		}
		if (s.x + 1 <= MAXN && !visited[s.x + 1])
		{
			q.push(Step(s.x + 1, s.steps + 1));
			visited[s.x + 1] = 1;
		}
		if (s.x * 2 <= MAXN && !visited[s.x * 2])
		{
			q.push(Step(s.x * 2, s.steps + 1));
			visited[s.x * 2] = 1;
		}
		q.pop();
	}
	return 0;
}

迷宫问题

#include
using namespace std;
int a[5][5];
int dis[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };
struct node
{
public:
	int x, y, pre;
}q[30];
int head = 0, tail = 0;
int visit[5][5];
void bfs()
{
	q[0].x = 0;	q[0].y = 0;	q[0].pre = -1;//起始位置的父节点设-1
	tail += 1;
	visit[0][0] = 1;
	while (head < tail)
	{
		for (int i = 0; i < 4; i++)
		{
			int x = q[head].x + dis[i][0];//遍历上下左右四个位置
			int y = q[head].y + dis[i][1];
			if (x > -1 && x < 5 && y > -1 && y < 5 && !a[x][y] && !visit[x][y])//xy均在范围内且能走且未被搜索过
			{
				q[tail].x = x;
				q[tail].y = y;
				q[tail].pre = head;
				visit[x][y] = 1;
				tail++;
				if (x == 4 && y == 4)
				{
					tail--; return;//到终点了
				}
			}
		}
		head++;
	}
}
void print(node que)
{
	if (que.pre == -1)
		cout << "(" << que.x << ", " << que.y << ")" << endl;
	else {
		print(q[que.pre]);
		cout << "(" << que.x << ", " << que.y << ")" << endl;
	}
}
int main()
{
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			cin >> a[i][j];
		}
	}
	bfs();
	print(q[tail]);
	return 0;
}

八数码问题

7种方法求解八数码问题yqw9999

贪心

只按某种指标选取最优

圣诞老人的礼物

如果糖果只能整箱拿,则不可使用贪心算法

#include
#include
using namespace std;
struct Candy
{
	int v, w;
	bool operator<(const Candy& c)
	{
		return double(v) / w - double(c.v) / c.w > 1e-6;
	}
}candies[110];
int main()
{
	int n, w;
	cin >> n >> w;
	for (int i = 0; i < n; i++)
		cin >> candies[i].v >> candies[i].w;
	sort(candies, candies + n);
	int totalW = 0;
	double totalV = 0;
	for (int i = 0; i < n; i++)
	{
		if (totalW + candies[i].w <= w)
		{
			totalW += candies[i].w;
			totalV += candies[i].v;
		}
		else
		{
			totalV += candies[i].v * double(w - totalW) / candies[i].w;
			break;
		}
	}
	cout << totalV << endl;
	return 0;
}

电影节

#include
#include
using namespace std;
struct Time
{
	int s;
	int e;
	bool operator<(const Time& t)
	{
		return t.e > e;
	}
}T[100];
int main()
{
	int t; cin >> t;
	while (t != 0)
	{
		for (int i = 0; i < t; i++)
		{
			cin >> T[i].s >> T[i].e;
		}
		sort(T, T + t);
		int time = T[0].e;
		int ans = 1;
		for (int i = 1; i < t; i++)
		{
			if (T[i].s >= time)
			{
				ans++;
				time = T[i].e;
			}
		}
		cout << ans << endl;
		cin >> t;
	}
	return 0;
}

Stall Reservations

#include
#include
#include
using namespace std;
struct Cow
{
	int a, b;
	int No;
	bool operator<(const Cow& c)const
	{
		return a < c.a;
	}
}cows[50100];
int pos[50100];
struct Stall
{
	int end;
	int No;
	bool operator<(const Stall& s)const
	{
		return end > s.end;
	}
	Stall(int e, int n) :end(e), No(n) {}
};
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> cows[i].a >> cows[i].b;
		cows[i].No = i;
	}
	sort(cows, cows + n);//按开始时间从小到大排序
	int total = 0;
	priority_queue<Stall>pq;
	for (int i = 0; i < n; i++)
	{
		if (pq.empty())
		{
			total++;
			pq.push(Stall(cows[i].b, total));
			pos[cows[i].No] = total;
		}
		else
		{
			Stall st = pq.top();//挑结束时间最早的出来
			if (st.end < cows[i].a)//最早结束时间小于下一只牛的开始时间
			{
				pq.pop();//修改最早时间
				pos[cows[i].No] = st.No;
				pq.push(Stall(cows[i].b, st.No));
			}
			else
			{
				total++;
				pq.push(Stall(cows[i].b, total));
				pos[cows[i].No] = total;
			}
		}
	}
	cout << total << endl;
	for (int i = 0; i < n; i++)
		cout << pos[i] << endl;
	return 0;
}

Radar Installation

#include
#include
#include
using namespace std;
struct node
{
	int x;
	int y;
	double x1, x2;
	bool operator <(const node& n)
	{
		return x1 < n.x1;
	}
}N[1005];
int main()
{
	int n, d;
	cin >> n >> d;
	int Case = 1;
	while (n != 0)
	{
		int flag = 0;
		for (int i = 0; i < n; i++)
		{
			double x,y,x1,x2;
			cin >> x >> y;
			N[i].x = x;	N[i].y = y;
			if (d * d * 1.0 - y * y < 0)
			{
				flag = 1;
				break;
			}
			x1 = x - sqrt(d * d * 1.0 - y * y);
			x2 = x + sqrt(d * d * 1.0 - y * y);
			N[i].x1 = x1;N[i].x2 = x2;
		}
		if (flag)
		{
			cout << "Case " << Case << ': ' << -1 << endl;
			continue;
		}
		sort(N, N + n);
		int ans = 1;
		int firstNoCovered = 0;
		double head=N[0].x1, tail=N[0].x2;
		for (int i = 1; i < n; i++)
		{
			if (N[i].x1 <= tail&&N[i].x1>=head)
			{
				if (N[i].x2 < tail)
					tail = N[i].x2;
			}
			else
			{
				ans++;
				head = N[i].x1;
				tail = N[i].x2;
				firstNoCovered = i;
			}
		}
		cout << "Case " << Case << ": " << ans << endl;
		cin >> n >> d;
		Case++;
	}
	return 0;
}

钓鱼

#include
#include
using namespace std;
int Time[105];
struct lake
{
	int num;
	int lose;
	bool operator<(const lake& a)const
	{
		return num < a.num;
	}
}Lake[30];
int main()
{
	int n, h;
	cin >> n >> h;
	h = h * 60 / 5;
	for (int i = 1; i <= n; i++)cin >> Lake[i].num;
	for (int i = 1; i <= n; i++)cin >> Lake[i].lose;
	for (int i = 1; i <= n - 1; i++)cin >> Time[i + 1];
	int ans = 0;
	priority_queue<lake>q;
	for (int i = 1; i <= n; i++)
	{
		int sum = 0;
		while (!q.empty())
			q.pop();
		for (int j = 1; j <= i; j++)
			q.push(Lake[j]);
		int t = 0;
		for (int j = 1; j <= i; j++)
			t += Time[j];
		for (int j = 0; j < h - t; j++)
		{
			lake L = q.top();
			q.pop();
			if (L.num > 0)
				sum += L.num;
			L.num -= L.lose;
			q.push(L);
		}
		if (sum > ans)ans = sum;
	}
	cout << ans << endl;
	return 0;
}

你可能感兴趣的:(算法,c++,数据结构)