Max Sum Plus Plus( HDU - 1024) 题目链接
dp[i][j] = max(dp[i ][j - 1] + a[j],dp[i - 1][k] + a[j]) (i - 1 <= k < j)
dp[i ][j - 1] 代表 与 a[j]是连在前一段的
dp[i - 1][k]代表a[j]自成一段
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int a[N];
int pre[N]; /// dp[i - 1][k] i - 1 < k < j
int dp[N];
int main()
{
int n,m;
while(scanf("%d %d",&m,&n) == 2){
for (int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
int mx;
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
for (int i = 1; i <= m; i++){
mx = -INF;
for (int j = i; j <= n; j++){
dp[j] = max(dp[j - 1],pre[j - 1]) + a[j];
pre[j - 1] = mx; /// 下一层的
mx = max(mx,dp[j]);
}
}
printf("%d\n",mx);
}
return 0;
}
Monkey and Banana (HDU - 1069) 题目链接
n种类型的长方体,可叠成的最大高度(底部的长宽都要大于上一层的)
一个长方体可以有三种长宽高
排个序就是基本dp的写法
#include <bits/stdc++.h>
using namespace std;
int dp[111]; // dp[i] 代表放第i个的最大值
struct node{
int x,y,z;
bool operator < (const node& p) const{
if (x > p.x) return true;
if (x == p.x && y > p.y) return true;
return false;
}
}q[99];
void xyswap(int n) /// 保证 x > y
{
for (int i = 1; i <= n; i++){
if (q[i].x < q[i].y) swap(q[i].x,q[i].y);
}
}
void add(int i)
{
q[i + 1].x = q[i].x;
q[i + 1].y = q[i].z;
q[i + 1].z = q[i].y;
q[i + 2].x = q[i].y;
q[i + 2].y = q[i].z;
q[i + 2].z = q[i].x;
}
int main()
{
int n;
int cnt = 1;
while (scanf("%d",&n) == 1 && n){
n *= 3;
for (int i = 1; i <= n; i += 3){
scanf("%d %d %d",&q[i].x,&q[i].y,&q[i].z);
add(i);
}
xyswap(n);
sort(q + 1,q + 1 + n);
// for (int i = 1; i <= n; i++){
// printf("%d %d %d\n",q[i].x,q[i].y,q[i].z);
// }
memset(dp,0,sizeof(dp));
for (int i = 1; i <= n; i++){
dp[i] = q[i].z;
}
int ans = q[1].z;
for (int i = 2; i <= n; i++){
for (int j = 1; j < i; j++){
if (q[i].x < q[j].x && q[i].y < q[j].y){
if (dp[i] < dp[j] + q[i].z){
dp[i] = dp[j] + q[i].z;
ans = max(ans,dp[i]);
}
}
}
}
printf("Case %d: maximum height = %d\n",cnt++,ans);
}
return 0;
}
Doing Homework (HDU - 1074)题目链接
状压DP
例如转移到 101001 可以 001001、100001、101000三种方式,以此类推
其他的就和普通DP类似
#include <bits/stdc++.h>
using namespace std;
const int INF = 1 << 30;
struct node{
string name;
int en,cost;
}s[55];
struct DP{
int time,score,pre,now;
}dp[1 << 15];
int main()
{
int t;
scanf("%d",&t);
while (t--){
int n;
scanf("%d",&n);
for (int i = 0; i < n; i++){
cin >> s[i].name >> s[i].en >> s[i].cost;
}
int k = 1 << n;
memset(dp,0,sizeof(dp));
for (int i = 1; i < k; i++){
dp[i].score = INF;
for (int j = n - 1; j >= 0; j--){
int tem = 1 << j;
if (i & tem){
int past = i - tem;
int st = dp[past].time + s[j].cost - s[j].en;
if (st < 0) st = 0;
if (st + dp[past].score < dp[i].score){
dp[i].score = st + dp[past].score;
dp[i].now = j;
dp[i].pre = past;
dp[i].time = dp[past].time + s[j].cost;
}
}
}
}
stack<int> st;
int tem = k - 1;
printf("%d\n",dp[tem].score);
while (tem){
st.push(dp[tem].now);
tem = dp[tem].pre;
}
while (!st.empty()){
cout << s[st.top()].name << endl;
st.pop();
}
}
return 0;
}
Piggy-Bank (HDU - 1114)题目链接
初始重量为e,最大重量为f,输出装满f重量下的最小价值
背包问题
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int v[555],w[555];
int dp[11111];
int main()
{
int t;
scanf("%d",&t);
while(t--){
int e,f,n;
scanf("%d %d %d",&e,&f,&n);
f -= e;
for (int i = 0; i < n; i++){
scanf("%d %d",&v[i],&w[i]);
}
memset(dp,0x3f,sizeof(dp));
dp[0] = 0;
for (int i = 0; i < n; i++){
for (int j = w[i]; j <= f; j++){
dp[j] = min(dp[j],dp[j - w[i]] + v[i]);
}
}
if (dp[f] == INF) printf("This is impossible.\n");
else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[f]);
}
return 0;
}
FatMouse’s Speed (HDU - 1160)题目链接
老鼠越肥,速度越慢,找到最长
W[m[1]] < W[m[2]] < … < W[m[n]]
and
S[m[1]] > S[m[2]] > … > S[m[n]]
普通dp(就是需要判断两个条件)
#include <bits/stdc++.h>
using namespace std;
const int N = 1111;
struct node{
int w,v,id;
bool operator < (const node& p) const{
if (w == p.w) return v > p.v;
return w < p.w;
}
}s[N];
int pre[N];
int dp[N];
int main()
{
int cnt = 1;
while (scanf("%d %d",&s[cnt].w,&s[cnt].v) != EOF && (s[cnt].w || s[cnt].v)) s[cnt].id = cnt,cnt++;
sort(s + 1, s + cnt);
// memset(pre,0,sizeof(pre));
int ans = 1,index = 1;
for (int i = 1; i < cnt; i++){
dp[i] = 1;
for (int j = 1; j < i; j++){
if (s[j].w < s[i].w && s[j].v > s[i].v){
if (dp[j] + 1 > dp[i]){
dp[i] = dp[j] + 1;
pre[i] = j;
if (dp[i] > ans){
ans = dp[i];
index = i;
}
}
}
}
}
printf("%d\n",ans);
stack<int> sa;
while(pre[index]){
sa.push(s[index].id);
index = pre[index];
}
printf("%d\n",s[index].id);
while(!sa.empty()){
printf("%d\n",sa.top());
sa.pop();
}
return 0;
}
Jury Compromise (POJ - 1015) 题目链接
题意
选出的m 个人,必须满足辩方总分D和控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的|D-P| 值相同,那么选辩控双方总分之和D+P最大的方案即可。
思路
这题可以采取0,1背包的思路,将选出人数看作背包容量,采取价值最小的解,同时规划价值最小的中价值最大的取法
状态转移方程 dp[i + 1][j + sub[k] = dp[i][j] + sum[k];
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
const int N = 22;
const int M = 2 * 20 * 20 + 5;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int sub[M],sum[M];
int dp[N][M];
vector<int> path[N][M];
int main()
{
int n,m;
int Case = 1;
while(scanf("%d %d",&n,&m) == 2 && (n && m)){
for (int i = 1; i <= n; i++){
int x,y;
scanf("%d %d",&x,&y);
sub[i] = x - y + 20;
sum[i] = x + y;
}
for (int i = 0; i <= m; i++){
for (int j = 0; j < 1007; j++){
path[i][j].clear();
}
}
memset(dp,-1,sizeof(dp));
int mx_size = 20 * m;
dp[0][0] = 0;
for (int k = 1; k <= n; k++){
for (int i = m - 1; i >= 0; i--){
for (int j = 0; j < 2 * mx_size; j++){
if (dp[i][j] >= 0 && dp[i + 1][j + sub[k]] <= dp[i][j] + sum[k]){
dp[i + 1][j + sub[k]] = dp[i][j] + sum[k];
path[i + 1][j + sub[k]] = path[i][j];
path[i + 1][j + sub[k]].push_back(k);
// cout << "sumk = " << sum[k] << "sub= " << sub[k] << " i+1 = " << i + 1 << " j + sub=" << j + sub[k] << " dp=" << dp[i + 1][j + sub[k]] << endl;
}
}
}
}
int index;
for (index = 0; dp[m][mx_size - index] == -1 && dp[m][mx_size + index] == -1; index++);
int sub_sum = dp[m][mx_size - index] > dp[m][mx_size + index] ? -index : index;
int add_sum = dp[m][mx_size + sub_sum];
// cout << "sub_sum = " << sub_sum << " add_sum = " << add_sum << endl;
int d = (sub_sum + add_sum) >> 1;
int p = (add_sum - sub_sum) >> 1;
printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n",Case++,d,p);
for (int i = 0; i < m; i++){
cout << path[m][mx_size + sub_sum][i] << " ";
}
cout << endl << endl;
}
return 0;
}
Common Subsequence (POJ - 1458)L题链接
找出两个字符串的公共子串最大长度
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 11111;
char x[N],y[N];
int dp[555][555];
int main()
{
while(scanf("%s %s",x,y) != EOF){
int len1 = strlen(x);
int len2 = strlen(y);
// cout << x << " " << y << endl;
memset(dp,0,sizeof(dp));
for (int i = 0; i < len1; i++){
for (int j = 0; j < len2; j++){
if (x[i] == y[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
}
}
printf("%d\n",dp[len1 - 1][len2 - 1]);
}
return 0;
}
Help Jimmy (POJ - 1661) 题目链接
场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。
Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。
设计一个程序,计算Jimmy到底地面时可能的最早时间。
dp[0][i]表示到达这个平台左边的时间
dp[1][i]表示到达平台右边的时间
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 1111;
const int INF = 0x3f3f3f3f;
struct node{
int l,r,h;
bool operator < (const node &p) const{
return h > p.h;
}
}s[N];
int dp[2][N],vis[N],vis2[N];
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,x,y,mx;
scanf("%d %d %d %d",&n,&x,&y,&mx);
for (int i = 1; i <= n; i++){
scanf("%d %d %d",&s[i].l,&s[i].r,&s[i].h);
}
sort(s + 1,s + 1 + n);
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
memset(vis2,0,sizeof(vis2));
s[0].h = y;
s[0].l = s[0].r = x;
for (int i = 1; i <= n; i++){
dp[0][i] = dp[1][i] = INF;
for (int j = 0; j < i; j++){
if (s[j].h - s[i].h > mx) continue;
if (s[i].l <= s[j].l && s[j].l <= s[i].r && !vis[j]){
vis[j] = 1;
dp[0][i] = min(dp[0][i],dp[0][j] + s[j].l - s[i].l + s[j].h - s[i].h);
dp[1][i] = min(dp[1][i],dp[0][j] + s[i].r - s[j].l + s[j].h - s[i].h);
}
if (s[i].l <= s[j].r && s[j].r <= s[i].r && !vis2[j]){
vis2[j] = 1;
dp[0][i] = min(dp[0][i],dp[1][j] + s[j].r - s[i].l + s[j].h - s[i].h);
dp[1][i] = min(dp[1][i],dp[1][j] + s[i].r - s[j].r + s[j].h - s[i].h);
}
}
// printf("i = %d dp0 = %d dp1 = %d\n",i,dp[0][i],dp[1][i]);
}
int ans = INF;
for (int i = 1; i <= n; i++){
if (s[i].h > mx) continue;
// printf("i = %d dp0 = %d dp1 = %d sj = %d\n",i,dp[0][i],dp[1][i],s[i].h);
if (!vis[i]) ans = min(ans,dp[0][i] + s[i].h);
if (!vis2[i]) ans = min(ans,dp[1][i] + s[i].h);
}
if (ans == INF) ans = y; /// 特判直接落到地面的情况
printf("%d\n",ans);
}
return 0;
}
Longest Ordered Subsequence (POJ - 2533)题目链接
LIS模板题
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 1111;
const int INF = 0x3f3f3f3f;
int a[N],b[N];
void solve(int &n)
{
int len = 0;
b[0] = -INF;
for (int i = 1; i <= n; i++){
if (a[i] > b[len]){
b[++len] = a[i];
}
else {
int l = 0,r = len;
while(l <= r){
int mid = (l + r) >> 1;
if (a[i] > b[mid]) l = mid + 1;
else r = mid - 1;
}
b[l] = a[i];
}
}
printf("%d\n",len);
}
int main()
{
int n;
while(scanf("%d",&n) == 1){
for (int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
solve(n);
}
return 0;
}
Treats for the Cows (POJ - 3186)题目链接
给n个数,排成一排,每次可以从队头或者队尾拿一个数,权值为第几个拿的*这个数,求最大和
从区间为一个长度往区间[1,n]进行dp
dp[i][j] 表示还剩下[i,j]这个区间可以取得的权值
dp[i][j] = max(dp[i + 1][j] + cnt * a[i],dp[i][j - 1] + cnt * a[j]) /// cnt 代表第几次拿
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 2222;
const int INF = 0x3f3f3f3f;
int a[N];
int dp[N][N];
int main()
{
int n;
while(scanf("%d",&n) == 1){
memset(dp,0,sizeof(dp));
for (int i = 1; i <= n; i++){
scanf("%d",&a[i]);
dp[i][i] = a[i] * n;
// printf("dpii = %d\n",dp[i][i]);
}
for (int i = 1; i < n; i++){
for (int j = 1; j + i <= n; j++){
int len = n - i;
dp[j][j + i] = max(dp[j + 1][j + i] + len * a[j],dp[j][j + i - 1] + len * a[j + i]);
// printf("j = %d j + i = %d dp = %d\n",j,j + i,dp[j][j + i]);
}
}
printf("%d\n",dp[1][n]);
}
return 0;
}
FatMouse and Cheese (HDU - 1078)题目链接
dfs和剪枝还是没学到家
#include <bits/stdc++.h>
using namespace std;
const int N = 111;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n,k;
int a[N][N];
int dp[N][N];
int to[4][2] = {0,1,0,-1,1,0,-1,0};
int dfs(int x,int y)
{
if(dp[x][y]) return dp[x][y];
int ans = 0;
for (int j = 1; j <= k; j++){
for (int i = 0; i < 4; i++){
int x1 = x + j * to[i][0];
int y1 = y + j * to[i][1];
if (x1 < 0 || x1 >= n || y1 < 0 || y1 >= n) continue;
if (a[x1][y1] <= a[x][y]) continue;
ans = max(ans,dfs(x1,y1));
}
}
return dp[x][y] = ans + a[x][y];
}
int main()
{
while(scanf("%d %d",&n,&k) == 2){
if (n == -1 && k == -1) break;
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
scanf("%d",&a[i][j]);
}
}
memset(dp,0,sizeof(dp));
int ans = dfs(0,0);
cout << ans << endl;
}
return 0;
}
Dollar Dayz (POJ - 3181) 题目链接
1-k种权值,能够有多少种凑成n的方式
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1111;
const int INF = 0x3f3f3f3f;
typedef long long ll;
const ll mod = 1e18;
ll dp[N][N];
ll dp2[N][N];
int main()
{
int n,k;
while(scanf("%d %d",&n,&k) == 2){
memset(dp,0,sizeof(dp));
memset(dp2,0,sizeof(dp2));
for (int i = 0; i <= k; i++){
dp2[i][0] = 1;
}
for (int i = 1; i <= k; i++){
for (int j = 1; j <= n; j++){
// dp[i][j] += dp[i - 1][j];
// if (j > i) dp[i][j] += dp[i][j - i];
if (j >= i){
dp[i][j] = dp[i - 1][j] + dp[i][j - i] + (dp2[i - 1][j] + dp2[i][j - i]) / mod;
dp2[i][j] = (dp2[i - 1][j] + dp2[i][j - i]) % mod;
}
else {
dp[i][j] = dp[i - 1][j];
dp2[i][j] = dp2[i - 1][j];
}
}
}
if (dp[k][n]) cout << dp[k][n];
cout << dp2[k][n] << endl;
}
return 0;
}
Milking Time (POJ - 3616)题目链接
01背包问题,就是把重量改成了区间
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;
const int M = 1111;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int dp[N];
struct node{
int s,e,v;
bool operator < (const node &p) const{
if (s == p.s) return e < p.e; /// s,e排序的顺序可以交换,不影响结果
return s < p.s;
}
}q[M];
int main()
{
int n,m,r;
while(scanf("%d %d %d",&n,&m,&r) == 3){
int mx = -1;
memset(dp,0,sizeof(dp));
for (int i = 0;i < m;i++){
scanf("%d %d %d",&q[i].s,&q[i].e,&q[i].v);
mx = max(mx,q[i].e);
}
mx = min(mx,n);
sort(q, q + m);
for (int i = 0; i < m; i++){
for (int j = mx; j >= q[i].e; j--){
int t = q[i].s - r;
if (t < 0) t = 0;
dp[j] = max(dp[j],dp[t] + q[i].v);
}
}
cout << dp[mx] << endl;
}
return 0;
}
Phalanx HDU - 2859题目链接
最大对称子图
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
char mat[N][N];
int dp[N][N];
int main()
{
int n;
while(scanf("%d",&n) == 1 && n){
memset(mat,0,sizeof(mat));
memset(dp,0,sizeof(dp));
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
scanf(" %c",&mat[i][j]);
}
}
int ans = 0;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
int x = i;
int y = j;
int cnt = 0;
while(mat[x][j] == mat[i][y]){
x--;
y++;
cnt++;
}
if (cnt > dp[i - 1][j + 1]) dp[i][j] = dp[i - 1][j + 1] + 1;
else dp[i][j] = cnt;
ans = max(ans,dp[i][j]);
}
}
cout << ans << endl;
}
return 0;
}
Making the Grade POJ - 3666 题目链接
给出长度为n的整数数列,每次可以将一个数加1或者减1,最少要多少次可以将其变成单调增或者单调减(不严格).
结果序列肯定和原序列的数字相同(不会证明)
dp[i][j] 表示 前i个数,并且第i个数变成第j个大的数的费用
dp[i][j] = min(dp[i-1][1…j]) + abs(a[i] - w[j]);
题目有点问题,只要求不递减即可
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2e3 + 5;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int a[N],w[N];
ll dp[N][N];
int main()
{
int n;
while(scanf("%d",&n) == 1 && n){
for (int i = 1; i <= n; i++){
scanf("%d",&a[i]);
w[i] = a[i];
}
sort(w + 1,w + 1 + n);
for (int i = 1; i <= n; i++){
ll mi = INF;
for (int j = 1; j <= n; j++){
mi = min(mi,dp[i - 1][j]);
dp[i][j] = abs(a[i] - w[j]) + mi;
}
}
ll ans = INF;
for (int i = 1; i <= n; i++){
ans = min(ans,dp[n][i]);
}
cout << ans << endl;
}
return 0;
}