本文将省略过多的赘述动态规划的基本原理,主要谈谈笔者在复习过程中对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;
}
热身篇结束!