计算概论A代码总结Ⅲ(动态规划类-热身篇)

本文将省略过多的赘述动态规划的基本原理,主要谈谈笔者在复习过程中对DP的理解。

1.护林员盖房子


//护林员盖房子//这和城堡问题不同:毕竟城堡问题是在寻找最大的空间,可以用深搜染色实现而,而护林员盖房子有独特的搜索矩形的算法
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 100;
int chessMap[maxn][maxn] = { 0 };
int main() {
	int r, c;
	cin >> r >> c;
	int a;
	memset(chessMap, 0, sizeof(chessMap));
	for (int i = 1; i <= r; i++) {
		for (int j = 1; j <= c; j++) {
			cin >> a;
			if (a == 0)chessMap[i][j] = 1;
			else {
				chessMap[i][j] = 0;
			}
		}
	}
	for (int i = 1; i <= r; i++) {
		for (int j = 1; j <=c; j++) {//细节勿出错,一定要先花五分钟写好思路再写,不然debug会很花时间
			if (chessMap[i][j] != 0)chessMap[i][j] = chessMap[i][j-1] + 1;
		}
	}
	int width; int area ; int maxarea = 0; int minlength;
	for (int i = 1; i <= r; i++) {
		for (int j = 1; j <= c; j++) {
			if (chessMap[i][j] != 0) {
				width = 1;//每次循环初始化变量
				area = 0;
				minlength = chessMap[i][j];
				area = width * chessMap[i][j];
				maxarea = max(maxarea, area);
				//然后开始向上搜索
				for (int k = i - 1; k >= 1; k--) {//再三注意循环条件
					if (chessMap[k][j] == 0)break;
					minlength = min(chessMap[k][j], minlength);
					width++;
					area = minlength * width;
					maxarea = max(maxarea, area);
				}
				maxarea = max(maxarea, area);//这一步往往是为了最后的更新
			}
		}
	}
	cout << maxarea << endl;
}

2.道路问题(poj典型)


道路问题,配备最优化剪枝和过程性剪枝
#include<iostream>
#include<memory.h>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 110;
struct Road {
	int d;//目的地
	int l;//长度
	int c;//花费
};
int mid[maxn][10010];
int num[maxn];//从这条路i出有几条路
int minlen;//当前的最小长度
int totallen;//当前总路程
int totalcost;//总花费
int K, N, R;
bool visit[maxn];//目的地是否去过
Road G[maxn][maxn];//第i个城市的第num[i】条路
void dfs(int s) {
	if (s == N) {
		minlen = min(minlen, totallen); return;
	}
	for (int i = 0; i < num[s]; i++) {
		Road r = G[s][i];
		if (totalcost + r.c > K) { continue; }
		if (!visit[r.d]) {
			if (totallen + r.l >= minlen) { continue; }
			if (totallen + r.l >= mid[r.d][totalcost + r.c]) { continue; }
			mid[r.d][totalcost + r.c] = totallen + r.l;//利用中间变量进行最优性剪枝
			visit[r.d] = 1;
			totallen += r.l;
			totalcost += r.c;
			dfs(r.d);
			totalcost -= r.c;
			totallen -= r.l;
			visit[r.d] = 0;
		}

	}
}
int main() {
	cin >> K >> N >> R;
	memset(num, 0, sizeof(num));
	for (int i = 0; i < maxn; i++) {
		for (int j = 0; j < 10010; j++) {
			mid[i][j] = 1 << 30;
		}
	}
	memset(visit, 0, sizeof(visit));
	for (int i = 0; i < R; i++) {
		Road r;
		int s;
		cin >> s >> r.d >> r.l >> r.c;
		if (s != r.d) {
			G[s][num[s]] = r;//直接保存一个节点
			num[s]++;
		}
	}
	visit[1] = 1;
	totalcost = 0;
	minlen = 1 << 30;
	totallen = 0;
	dfs(1);

	if (minlen < (1 << 30)) { cout << minlen << endl; return 0; }
	cout << "-1" << endl; return 0;

}


3.区间覆盖(也可以动归做,此处采用递归算法)

解法Ⅰ


区间覆盖
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 200;
struct line {
	long long left;
	long long right;
};
line s[maxn];
int cmp(line a,line b) {
	return a.left < b.left;
}
int visit[maxn]; int num;
int win; int n;
void find(int start, int end) {
	int maxn = 0;
	int flag = 0;
	int play = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i].left <= start&&s[i].right>start && !visit[i]) {
			flag = s[i].right > maxn ? i : flag;//先更新标志
			maxn = s[i].right > maxn ? s[i].right : maxn;
			play = 1;
		}
	}
	visit[flag] = 1;//标记用了
	start更新为maxn
	start = maxn;
	if (start >= end) { num++; win = 1; return; }
	if (play == 1) {
		num++;
		find(start, end); return;
	}
	return;
}
int main() {
	long long m;
	cin >> m;
	memset(visit, 0, sizeof(visit));
	int i = 1;
	int a = 0; int b = 0;
	while (cin >> a >> b) {
		if (a == 0 && b == 0)break;
		s[i].left = a; s[i].right = b;
		i++;
	}
	n = i - 1;
	sort(s + 1, s + n, cmp);
	num = 0; win = 0;
	find(0, m);
	if (win) { cout << num << endl; }
	else { cout << "-1"<<endl; }
}

解法Ⅱ

//区间覆盖
#include<iostream>
#include<algorithm>
using namespace std;
int n, m;
const int N = 100010;

struct _arrange {
	int l, r;
	bool operator < (_arrange A)
	{
		return l < A.l;
	}
} range[N];

bool success;
int main()
{
	int end, start;
	cin >> start >> end >> n;
	for (int i = 0; i < n; ++i)
		scanf("%d%d", &range[i].l, &range[i].r);
	sort(range, range + n);
	int ans = -2e9;
	int cnt = 0;

	for (int i = 0; i < n; ++i)
	{
		int  j = i, r = -2e9;
		while (j < n && range[j].l <= start)  // 在左边界小于start 的区间中选择右边界最大的
			r = max(r, range[j++].r);

		if (start > r)   // 或者写成 if(i == j) 如果没有区间的左边界小于start, r = -2e9 说明不连续
			break;

		cnt++;

		if (r >= end) // 说明区间已经超过了end
		{
			success = true;
			break;
		}
		start = r;
		i = j - 1;  // 这里可以改成i = j ,然后循环最后的i++就除去。
	}

	if (success) printf("%d", cnt);
	else printf("-1");
	return 0;
}

热身篇结束!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值