算法課複習 -- 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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章