算法课复习 -- dp

HDU #2602 : Bone Collector

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2602

题意:n个东西,每个东西都有它的大小和价值。给定最大容量v,问容量为v的情况下最大价值为多少。

思路:最基本的背包类,要化成一维数组做的话从dp[n]往dp[1]就可以。

dp[i][j]:前i个东西容量为j时的最大价值。

dp[i][j] = max( dp[i][j] , dp[i][j-v[i]] + cost[i] );

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;

const ll M = 1e9 + 7;
const ll INF = 1e9;
const double _e = 10e-6;
const int maxn = 1e5 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };

int x, y, z;
char c;

int t, n, v;
int dp[1010], v1[1010], v2[1010];

int main()
{
	scanf("%d", &t);
	while (t--) {
		cl0(dp);
		scanf("%d%d", &n, &v);
		for (int i = 1; i <= n; i++)
			scanf("%d", &v1[i]);
		for (int i = 1; i <= n; i++)
			scanf("%d", &v2[i]);
		for (int i = 1; i <= n; i++) {
			for (int j = v; j >= 0; j--) {
				if (j - v2[i] < 0)break;
				dp[j] = max(dp[j], dp[j - v2[i]] + v1[i]);
			}
		}
		printf("%d\n", dp[v]);
	}
	return 0;
}

 

OpenJ_Bailian #2533 : Longest Ordered Subsequence

传送门:http://bailian.openjudge.cn/practice/2533?lang=en_US

题意:给一个字符串,求它的最长上升子序列的长度。

思路:dp[i]:长度为i的上升序列中,第i位的最小值。

for循环,对于 dp[i-1] < s[j] 成立的最大的 i,dp[i] = min ( dp[i] , s[j] );

直接lower_bound二分来找更新位置也是可以的。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;

const ll M = 1e9 + 7;
const ll INF = 1e9;
const double _e = 10e-6;
const int maxn = 1e5 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };

int x, y, z;
char c;

int t, n, v;
int a[1010], dp[1010];

int main()
{
	while (~scanf("%d", &n)) {
		for (int i = 0; i <= 1005; i++)
			dp[i] = INF;
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		for (int i = 1; i <= n; i++) {
			int pos = lower_bound(dp + 1, dp + n + 1, a[i]) - dp;
			dp[pos] = a[i];
		}
		for (int i = 1; i <= n + 1; i++) {
			if (dp[i] == INF) {
				printf("%d\n", i - 1);
				break;
			}
		}		
	}
	return 0;
}

 

HDU #1159 : Common Subsequence

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1159

题意:给两个字符串,求最长公共子串。

思路:dp[i][j]:字符串s1从0-i位置,s2从0-j位置的公共子串的最大长度。

s[i]==s[j]:dp[i][j] = max ( dp[i][j] , dp[i-1][j-1] + 1 );

s[i]!=s[j]:dp[i][j] = max ( dp[i][j] , dp[i-1][j] , dp[i][j-1] );

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;

const ll M = 1e9 + 7;
const ll INF = 1e9;
const double _e = 10e-6;
const int maxn = 1e5 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };

int x, y, z;
char c;

string s1, s2;
int dp[1010][1010];

int main()
{
	while (cin >> s1 >> s2) {
		int len1 = s1.length(), len2 = s2.length();
		cl0(dp);
		for (int i = 0; i < len1; i++) {
			for (int j = 0; j < len2; j++) {
				if (s1[i] == s2[j]) {
					if (i == 0 || j == 0)
						dp[i][j] = 1;
					else 					
						dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
				}
				else {
					if (i != 0)
						dp[i][j] = max(dp[i][j], dp[i - 1][j]);
					if (j != 0)
						dp[i][j] = max(dp[i][j], dp[i][j - 1]);
				}
			}
		}
		printf("%d\n", dp[len1 - 1][len2 - 1]);
	}
	return 0;
}

 

HDU #5092 : Seam Carving

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5092

题意:给出一个n行m列的矩阵。从顶上往下走,如果开始在i列,则下次只能在i-1、i、i+1列中任意一列。每个位置有一个数字,问怎么走才能使得一路上的数字之和最小。如果有相同的话取最靠右的方案。

思路:dp + 用pre数组记路。

pre记路的话,每次 dp[i][j] 更新的时候 pre[i][j] 也跟着更新,在最后利用 ans=pre[i--][ans]; 来还原。

dp[i][j]:走到第 i 行,第 j 列时的最小值。

dp[1][j] = a[1][j];

dp[i][j] = min( dp[i][j] , dp[i-1][j+k] );  ( -1 <= k <= 1 )

注意因为相同取最右,所以如果是从左往右则小于等于的时候也要更新。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;

const ll M = 1e9 + 7;
const ll INF = 1e9;
const double _e = 10e-6;
const int maxn = 1e5 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };

int x, y, z;
char c;

int t, n, m;
ll a[110][110], dp[110][110];
int pre[110][110];

int main()
{
	scanf("%d", &t);
	for (int _ = 1; _ <= t; _++) {
		scanf("%d%d", &n, &m);
		cl0(pre);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++)
				dp[i][j] = INF;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++)
				scanf("%d", &a[i][j]);
		for (int i = 1; i <= m; i++)
			dp[1][i] = a[1][i];
		for (int i = 2; i <= n; i++)
			for (int j = 1; j <= m; j++) {
				for (int k = -1; k <= 1; k++) {
					if (j + k <= 0 || j + k > m)continue;
					if (dp[i - 1][j + k] + a[i][j] <= dp[i][j]) {
						dp[i][j] = dp[i - 1][j + k] + a[i][j];
						pre[i][j] = j + k;
					}
				}
			}
		int ans = 1;
		for (int i = 2; i <= m; i++) {
			if (dp[n][i] <= dp[n][ans])
				ans = i;
		}
		stack<int> s;
		while (ans != 0) {
			s.push(ans);
			ans = pre[n--][ans];
		}
		printf("Case %d\n", _);
		printf("%d", s.top()); s.pop();
		while (!s.empty()) {
			printf(" %d", s.top());
			s.pop();
		}
		printf("\n");
	}
	return 0;
}

 

UVA #10003 : Cutting Sticks

传送门:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=944

题意:给出木头总长度和需要切的点,每次切一块长度为n的木头需要花费n,问最少花费多少。

思路:区间dp。

dp[i][j]:点 i 到点 j 范围内的最小花费。点0为0位置,点n+1为l位置。

dp[i][j] = min ( dp[i][j] , dp[i][k] + dp[k][j] + cost[i][j] ); ( i < k < j )

最终答案为dp[0][n+1]。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;

const ll M = 1e9 + 7;
const ll INF = 1e9 + 10;
const double _e = 10e-6;
const int maxn = 1e4 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };

int x, y, z;
char c;

int l, n;
int a[110];
int dp[110][110];

int main()
{
	while (~scanf("%d", &l)) {
		if (l == 0)break;
		for (int i = 0; i <= 100; i++)
			for (int j = 0; j <= 100; j++) {
				dp[i][j] = INF;
				if (i == j)dp[i][j] = 0;
			}		
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		a[0] = 0; a[n + 1] = l;
		for (int i = 1; i <= n + 1; i++) {
			dp[i][i - 1] = 0;
			dp[i - 1][i] = 0;
		}
		for (int len = 2; len <= n + 1; len++) {
			for (int s = 0; s <= n + 1; s++) {
				int e = s + len;
				if (e > n + 1)break;
				for (int i = s + 1; i <= e - 1; i++)
					dp[s][e] = min(dp[s][e], dp[s][i] + dp[i][e] + a[e] - a[s]);
			}
		}
		printf("The minimum cutting is %d.\n", dp[0][n + 1]);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值