专题十二 基础DP

专题十二 基础DP

HDU 1024 Max Sum Plus Plus

大意:

从一序列中取出若干段,这些段之间不能交叉,使得和最大并输出。

思路:

动态规划 首先我们可以列出最基本的状态转移方程:

d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] + a [ j ] , d p [ i − 1 ] [ k ] + a [ j ] ) i − 1 < = k < = j − 1 dp[i][j] = max( dp[i][j-1] + a[j] , dp[i-1][k] + a[j ]) i-1<=k<=j-1 dp[i][j]=max(dp[i][j1]+a[j],dp[i1][k]+a[j])i1<=k<=j1

这个方程的含义是:

$dp[i][j] $是将前 j 个数分成 i 份,且第 i 份包含第 j 个数 的情况下的最大值

那么对于第 j 个数来说,就有两个选择:

作为第 i 份的一部分 :也就是将前 j-1 个数分成 i 份 且第 j-1 个数属于第 i 份 即 d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1]

或者单独出来成为第 i 份:也就是将前 j-1 个数分成 i-1 份 且第 j-1 个数不一定属于第 i-1 份 即 d p [ i − 1 ] [ k ] i − 1 < = k < = j − 1 dp[i-1][k] i-1<=k<=j-1 dp[i1][k]i1<=k<=j1

但是这个方程不仅时间复杂度高,空间复杂度也高的可怕 这是不行的

所以我们要将其优化:

首先我们发现$ dp[i][j] 只 需 要 比 较 只需要比较 dp[i][j-1] 与 与 dp[i-1][k] 的 最 大 值 即 可 而 这 个 的最大值即可 而这个 dp[i-1][k] $的最大值是可以记录下来的 不需要遍历 这就砍去了一层循环

所以我们只需要定义一个 pre[n] 数组 用 pre[j] 来存储第 j-1 个数被分成 i-1 份时的最大值即可

于此同时 在计算$ dp[i][j] 时 , 我 们 可 以 计 算 出 时,我们可以计算出 dp[i][k] i<=k<=j 的 值 而 这 个 值 是 在 之 后 我 们 要 计 算 的值 而这个值是在之后我们要计算 dp[i+1][j+1] $时 要使用的 pre[j]

现在状态转移方程变成了:
$  dp[i][j] = max( dp[i][j-1] + a[j] , pre[j-1] + a[j ]) $

现在我们发现 由于pre[j] 的存在 似乎已经不需要 $dp[i][j] 这 个 庞 大 的 二 维 数 组 了 只 需 要 开 一 个 d p [ n ] 的 数 组 用 d p [ j ] 来 存 储 这个庞大的二维数组了 只需要开一个 dp[n] 的数组 用dp[j]来存储 dp[n]dp[j]dp[i][j]$即可,因为当前的转移方程根本就没有用到 i 这一维!

这样的话 转移方程又变成了:
$dp[j] = max( dp[j-1] + a[j] , pre[j-1] + a[j ]) $

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int a[N], n, m;
LL pre[N], dp[N];
int main() {
    while (scanf("%d", &m) != EOF) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            pre[i] = 0;
            dp[i] = 0;
        }
        pre[0] = dp[0] = 0;
        LL anss,temp;
        for (int i = 1; i <= m; i++) {
            temp = -0x3f3f3f3f;
            for (int j = i; j <= n; j++) {
                dp[j] = max(dp[j - 1] + a[j], pre[j - 1] + a[j]);
                pre[j - 1] = temp;
                temp = max(temp, dp[j]);
            }
        }
        cout << temp << endl;
    }
    return 0;
}

HDU 1029 Ignatius and the Princess IV

大意:

求数列中出现次数大于n/2的数字

思路:

不需要dp…不知道为什么分到了这个专题里,直接求即可,优化了空间复杂度

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n;
int main(){
    while(cin>>n){
        int x, cnt, res;
        cin >> x;
        cnt = 1;
        res = x;
        n--;
        while(n--){
            cin>>x;
            if (x == res) cnt++;
            else cnt--;
            if(cnt<0){
                cnt = 1;
                res = x;
            }
        }
        cout << res << endl;
    }
    return 0;
}

HDU 1069 Monkey and Banana

大意:

给出n个种类的正方体,每种都有无穷多数量,现在要求搭建一个塔,从下到上用到的正方体是严格满足上面的边小于下面的边的,问最高能搭多高

思路:

首先需要将n个种类的正方体的六种摆放方式都存下来,然后 d p [ i ] dp[i] dp[i]代表以第i个正方体为顶的塔的高度,那么 n 2 n^2 n2去枚举,符合严格小于的条件就更新即可

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n;
struct node{
    int x, y, z;
} a[N];
bool cmp(node a,node b){
    if (a.x == b.x) return a.y > b.y;
    else return a.x > b.x;
}
int dp[N],t=0;
int main(){
    while(scanf("%d",&n)&&n!=0){
        t++;
        for (int i = 0; i < n;i++){
            cin >> a[i * 6].x >> a[i * 6].y >> a[i * 6].z;
            a[i * 6 + 1].x = a[i * 6].y, a[i * 6 + 1].y = a[i * 6].x, a[i * 6 + 1].z = a[i * 6].z;
            a[i * 6 + 2].x = a[i * 6].y, a[i * 6 + 2].y = a[i * 6].z, a[i * 6 + 2].z = a[i * 6].x;
            a[i * 6 + 3].x = a[i * 6].x, a[i * 6 + 3].y = a[i * 6].z, a[i * 6 + 3].z = a[i * 6].y;
            a[i * 6 + 4].x = a[i * 6].z, a[i * 6 + 4].y = a[i * 6].x, a[i * 6 + 4].z = a[i * 6].y;
            a[i * 6 + 5].x = a[i * 6].z, a[i * 6 + 5].y = a[i * 6].y, a[i * 6 + 5].z = a[i * 6].x;
        }
        sort(a, a + 6 * n, cmp);
        for (int i = 0; i < 6 * n;i++){
            dp[i] = a[i].z;
        } 
        int res = 0;
        for (int i = 0; i < 6 * n;i++){
            for (int j = i+1; j < 6*n;j++){
                if(a[j].x<a[i].x&&a[j].y<a[i].y)
                    dp[j] = max(dp[j], dp[i] + a[j].z);
                res = max(dp[i], res);
            } 
        }
        printf("Case %d: maximum height = %d\n",t,res); 
    }
    return 0;
}

HDU 1074 Doing Homework

大意:

给出n( n < = 15 n<=15 n<=15)个作业的用时和deadline,如果有作业超过deadline,那么每超过1天就要扣1分(超过多个作业也是1分),问扣分最少的方案,按字典序最小输出(题目保证输入按照字典序递增)

思路:

状压dp,枚举能转移过来的每个状态,更新dp数组即可

#include <bits/stdc++.h>

using namespace std;

const int N = 15 + 5;
typedef long long LL;
int T, n, d[N], t[N], dp[1 << N], l[1 << N], pre[1 << N], res[N], cnt,
    step[1 << N];
string name[N];
int main() {
    cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> name[i] >> d[i] >> t[i];
        }
        memset(dp, 0x3f, sizeof dp);
        memset(l, 0, sizeof l);
        dp[0] = 0;
        for (int i = 0; i < (1 << n); i++) {
            for (int j = 0; j < n; j++) {
                if ((1 << j) & i) {
                    int prestate = i - (1 << j);
                    int temp = max(0, l[prestate] + t[j] - d[j]);
                    if (dp[prestate] + temp <= dp[i]) {
                        dp[i] = dp[prestate] + temp;
                        l[i] = l[prestate] + t[j];
                        pre[i] = prestate;
                        step[i] = j;
                    }
                }
            }
        }
        cnt = 0;
        int state = ( 1 << n ) - 1;
        cout << dp[state] << endl;
        while (state != 0) {
            res[cnt++] = step[state];
            state = pre[state];
        }

        for (int i = cnt - 1; i >= 0; i--) {
            cout << name[res[i]] << endl;
        }
    }
    return 0;
}

HDU 1087 Super Jumping! Jumping! Jumping!

大意:

求最大权值上升子序列

思路:

把最大上升子序列的板子改改就行,dp[i]代表以i为结尾的上升子序列的权值

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n, a[N], dp[N];
int main() {
    while (scanf("%d", &n) && n != 0) {
        for (int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
            dp[i] = a[i];
        }
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (a[j] < a[i]) {
                    dp[i] = max(dp[j] + a[i], dp[i]);
                }
            }
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            res = max(res, dp[i]);
        }
        cout << res << endl;
    }
    return 0;
}

HDU 1114 Piggy-Bank

大意:

给出储钱罐的罐重和总重,以及m种硬币的数量和面值,问符合条件(即硬币重量=总重-罐重)的硬币最小的面值和为多少

思路:

完全背包,容量为总重-罐重,价值为硬币的面值

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4 + 5;
typedef long long LL;
int T, n, v, dp[N], m, s[N], vul[N];
int main() {
    cin >> T;
    while (T--) {
        int x, y;
        cin >> x >> y;
        v = y - x;
        cin >> m;
        for (int i = 0; i < m; i++) {
            cin >> vul[i] >> s[i];
        }
        memset(dp, 0x3f, sizeof dp);
        dp[0] = 0;
        for (int i = 0; i < m; i++) {
            for (int j = s[i]; j <= v; j ++) {
                dp[j] = min(dp[j], dp[j - s[i]] + vul[i]);
            }
        }
        if(dp[v]==0x3f3f3f3f){
            cout << "This is impossible." << endl;
        }
        else{
            cout << "The minimum amount of money in the piggy-bank is " << dp[v] << "." << endl;
        }
    }
    return 0;
}

HDU 1176 免费馅饼

大意:

有n个馅饼在不同的时间会落到0到10的区间内,初始位置为5,每秒只能移动一米,问最多能接到多少馅饼

思路:

既可以正着写也可以反着写,正着写的话需要判断能否达到这个点,反着写就无所谓了

正着写:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;
typedef long long LL;
int n, mp[N][20], dp[N][20];
int main() {
    while (scanf("%d", &n) && n != 0) {
        int x, t;
        int lastt = 0;
        memset(mp, 0, sizeof mp);
        memset(dp, 0xc0, sizeof dp);
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &x, &t);
            mp[t][x]++;
            lastt = max(lastt, t);
        }
        dp[0][5] = 0;
        for (int i = 1; i <= lastt; i++) {
            for (int j = 0; j <= 10; j++) {
                if (j != 0)
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]);
                if (j != 10)
                    dp[i][j] = max(dp[i][j], max(dp[i-1][j],dp[i - 1][j + 1]));
                if(dp[i][j]!=int(0xc0c0c0c0)){
                    //cout << dp[i][j] << endl;
                    dp[i][j] += mp[i][j];
                }
                //cout << i << ' ' << j << ' ' << dp[i][j] << endl;
            }

        }
        int res = 0;
        for (int i = 0; i <= 10;i++){
            res = max(res, dp[lastt][i]);
        }
        printf("%d\n", res);
    }
    return 0;
}

反着写:

#include<bits/stdc++.h>

using namespace std;

int i,j,n,dp[100005][20],maxn,x,t;

int main()
{
	while(scanf("%d",&n),n!=0)
	{
		memset(dp,0,sizeof(dp));
		maxn=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d%d",&x,&t);
			dp[t][x]++;
			if(t>maxn)
			maxn=t;
		}
		for(i=maxn-1;i>=0;i--)
		{
			dp[i][0]+=max(dp[i+1][0],dp[i+1][1]);
			dp[i][10]+=max(dp[i+1][10],dp[i+1][9]);
			for(j=1;j<=9;j++)
			{
				dp[i][j]+=max(dp[i+1][j],max(dp[i+1][j-1],dp[i+1][j+1]));
			}
		}
		printf("%d\n",dp[0][5]);
	}
}

HDU 1260 Tickets

大意:

给出n个人买票的时间以及他们每个人和下一个人合买双人票的时间,问最早什么时候能卖完票

思路:

d p [ i ] dp[i] dp[i]代表前i个人买完票需要多久,那么可以从 d p [ i − 2 ] dp[i-2] dp[i2]转移过来,也可以从 d p [ i − 1 ] dp[i-1] dp[i1]转移过来

#include <bits/stdc++.h>

using namespace std;

const int N = 2e4 + 5;
typedef long long LL;
int t, n, a[N], b[N], dp[N];
int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            dp[i] = 0x3f3f3f3f;
        }
        for (int i = 0; i < n - 1; i++) {
            cin >> b[i];
        }
        for (int i = 0; i < n; i++) {
            if (i == 0) dp[i] = a[0];
            else if (i == 1) dp[i] = min(a[0] + a[1], b[0]);
            else dp[i] = min(dp[i - 2] + b[i - 1], dp[i - 1] + a[i]);
        }
        int res = dp[n-1];
        int ss = res % 60;
        int mm = (res / 60) % 60;
        int hh = ((res / 60) / 60) % 60;
        if (hh + 8 > 12) printf("%02d:%02d:%02d pm\n", hh - 4, mm, ss);
        else printf("%02d:%02d:%02d am\n", hh + 8, mm, ss);
    }
    return 0;
}

HDU 1257 最少拦截系统

大意:

经典题,最长上升子序列

思路:

模板题

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n, a[N], dp[N];
int main() {
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i < n; i++) scanf("%d", &a[i]), dp[i] = 1;
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (a[j] < a[i]) dp[i] = max(dp[j] + 1, dp[i]);
                res = max(res, dp[i]);
            }
        }
        cout << res << endl;
    }
    return 0;
}

HDU 1160 FatMouse’s Speed

大意:

给出n个老鼠的体重x和速度y,要求找出最多的一组老鼠,使他们严格符合体重上升,速度下降

思路:

先按照体重排一下序,然后求最长下降子序列即可,不过需要记录路径,开一个pre数组即可

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4 + 5;
typedef long long LL;
int cnt, dp[N], res, pre[N];
struct node {
    int x, y, id;
} a[N];
bool cmp(node a, node b) {
    if (a.x == b.x) return a.y > b.y;
    return a.x < b.x;
}
int main() {
    while (scanf("%d%d", &a[cnt].x, &a[cnt].y) != EOF) {
        dp[cnt] = 1;
        pre[cnt] = -1;
        cnt++;
        a[cnt-1].id = cnt;
    }
    int ed = 0;
    sort(a, a + cnt, cmp);
    for (int i = 0; i < cnt; i++) {
        for (int j = 0; j < i; j++) {
            if (a[j].y > a[i].y&&a[i].x>a[j].x) {
                if (dp[j] + 1 > dp[i]){
                    pre[i] = j;
                    dp[i] = dp[j] + 1;
                } 
            }
        }
        if (dp[i] > res) {
            res = dp[i];
            ed = i;
        }
    }
    cout << res << endl;
    stack<int> s;
    while (ed != -1) {
        s.push(a[ed].id);
        ed = pre[ed];
    }
    while(!s.empty()){
        cout << s.top() << endl;
        s.pop();
    }
    return 0;
}

POJ 1015 Jury Compromise

大意:

n个候选人,从中选出m个人。

控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。

思路:

d p [ i ] [ k ] dp[i][k] dp[i][k]代表选了i个人且控辩差为k的情况下,控辩和的最大值。

为了处理方便,需要将k都加上m*20,这样保证所有的k都是大于等于0的。

初始将dp数组赋值为-1, d p [ 0 ] [ m ∗ 20 ] = 0 dp[0][m*20]=0 dp[0][m20]=0,只有当dp数组不等于-1时可以通过这个状态转移,同时需要保证要选的人之前没有选过,那么只需要维护一个pre数组记录达到当前状态选了哪些人即可

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;

const int N = 200 + 5;
typedef long long LL;
int n, m, a[N], b[N], dp[25][1000], maxk, pre[25][1000], res[25];
int cases = 0;
int main() {
    while (cin >> n >> m && (n + m != 0)) {
        cases++;
        memset(dp, -1, sizeof dp);
        memset(pre, -1, sizeof pre);
        for (int i = 0; i < n; i++) {
            cin >> a[i] >> b[i];
        }
        maxk = m * 20;
        dp[0][maxk] = 0;
        for (int i = 0; i < m; i++)
            for (int k = 0; k <= maxk * 2; k++)
                if (dp[i][k] != -1)
                    for (int j = 0; j < n; j++) {
                        if (a[j] + b[j] + dp[i][k] >
                            dp[i + 1][k + a[j] - b[j]]) {
                            int x = i, y = k;
                            while (x > 0 && pre[x][y] != j) {
                                y = y - (a[pre[x][y]] - b[pre[x][y]]);
                                x--;
                            }
                            if (x == 0) {  // j不在路径内
                                pre[i + 1][k + a[j] - b[j]] = j;
                                dp[i + 1][k + a[j] - b[j]] =
                                    a[j] + b[j] + dp[i][k];
                            }
                        }
                    }
        int k = 0;
        for (k = 0; k <= maxk; k++) {
            if (dp[m][k + maxk] != -1) break;
            if (dp[m][-k + maxk] != -1) break;
        }
        if (dp[m][k + maxk] > dp[m][-k + maxk])
            k = k + maxk;
        else
            k = maxk - k;
        cout << "Jury #" << cases << endl;
        cout << "Best jury has value " << (k - maxk + dp[m][k]) / 2
             << " for prosecution and value " << (dp[m][k] - k + maxk) / 2
             << " for defence: " << endl;
        for (int i = m; i > 0; i--) {
            res[i-1] = pre[i][k]+1;
            k = k - (a[pre[i][k]] - b[pre[i][k]]);
        }
        sort(res, res + m);
        for (int i = 0; i < m; i++) cout << ' ' << res[i];
        cout << endl;
    }
    return 0;
}

POJ 1458 Common Subsequence

lcs板子题

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e3 + 5;
string s1, s2;
int dp[N][N];
int main() {
    while (cin >> s1 >> s2) {
        memset(dp, 0, sizeof dp);
        s1 = " " + s1;
        s2 = " " + s2;
        for (int i = 1; i < s1.size(); i++) {
            for (int j = 1; j < s2.size(); j++) {
                if (s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
                else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        cout << dp[s1.size()-1][s2.size()-1] << endl;
    }
}

POJ 1661 Help Jimmy

大意:

一个小球从 ( x , y ) (x,y) (x,y)位置落下,下落速度恒定为1,当落到一个平台时可以向左也可以向右走,速度也是1,走到边缘时继续下落,每次下落距离不能超过k米,现在给出n个平台的左右边缘位置和高度,问小球最快多久能落到地面

思路:

首先将平台按照高度排一下序,然后计算一下小球第一次碰到的是哪个平台,然后从这个平台开始更新dp数组。 d p [ i ] [ 0 ] dp[i][0] dp[i][0]代表到达第i个平台的左边缘最快时间是多少, d p [ 0 ] [ 1 ] dp[0][1] dp[0][1]代表到达右边缘。

可以从上到下写也可以从下到上写,从上到下写需要判断dp数组是否为INF,如果为INF代表不能到达第i个平台,那么就不能利用这个平台去更新下面的平台

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e3 + 5;
typedef long long LL;
int t, n, x, y, k, start = 0,dp[N][2],res;
struct node {
    int l, r, h;
} a[N];
const int INF = 0x3f3f3f3f;
bool cmp(node a, node b) { return a.h > b.h; }

int main() {
    cin >> t;
    while (t--) {
        cin >> n >> x >> y >> k;
        for (int i = 0; i < n; i++) {
            cin >> a[i].l >> a[i].r >> a[i].h;
        }
        memset(dp, 0x3f, sizeof dp);
        sort(a, a + n, cmp);
        res = INF;
        a[n].l = -INF;
        a[n].r = INF;
        a[n].h = 0;
        for (int i = 0; i <= n; i++) {
            if (a[i].r < x || a[i].l > x || a[i].h > y || y - a[i].h > k)
                continue;
            else {
                start = i;
                break;
            }
        }
        if (start == n) {
            cout << y << endl;
            continue;
        }
        dp[start][0] = abs(y-a[start].h)+abs(x - a[start].l);
        dp[start][1] = abs(y-a[start].h)+abs(x - a[start].r);
        for (int i = start; i < n; i++) {
            if (dp[i][0] == INF) continue;
            int flag1 = 0;
            int flag2 = 0;
            for (int j = i + 1; j < n && (flag1 + flag2 != 2); j++) {
                if (a[j].r < a[i].l || a[j].l > a[i].r || a[i].h - a[j].h > k)
                    continue;
                else {
                    if(flag1==0){
                        if(a[j].l<=a[i].l){
                            flag1 = 1;
                            dp[j][0]=min(dp[j][0],dp[i][0]+abs(a[i].l-a[j].l)+abs(a[i].h-a[j].h));
                            dp[j][1]=min(dp[j][1],dp[i][0]+abs(a[i].l-a[j].r)+abs(a[i].h-a[j].h));
                        }
                    }
                    if(flag2==0){
                        if(a[j].r>=a[i].r){
                            flag2 = 1;
                            dp[j][0]=min(dp[j][0],dp[i][1]+abs(a[i].r-a[j].l)+abs(a[i].h-a[j].h));
                            dp[j][1]=min(dp[j][1],dp[i][1]+abs(a[i].r-a[j].r)+abs(a[i].h-a[j].h));
                        }
                    }
                }
            }
            if (flag1 == 0 && (a[i].h <= k)) res = min(res, dp[i][0] + a[i].h);
            if (flag2 == 0 && (a[i].h <= k)) res = min(res, dp[i][1] + a[i].h);
        }
        cout << res << endl;
    }
}

POJ 2533 Longest Ordered Subsequence

最长上升子序列模板题

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e3 + 5;
int n, a[N], dp[N], res;
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i], dp[i] = 1;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
        }
        res = max(res, dp[i]);
    }
    cout << res << endl;
}

POJ 3186 Treats for the Cows

大意:

给出n个数,每次只能从剩下的数里面取第一个数或者最后一个数,价值是 a [ i ] ∗ k a[i]*k a[i]k,k为第几次取,问最大价值和是多少

思路

区间dp, d p [ i ] [ j ] dp[i][j] dp[i][j]代表从剩下第i个数和到第j个数时,能取到的最大值

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 2e3 + 5;
int n, a[N], dp[N][N], res;
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i], dp[i][i] = n * a[i];
    for (int i = n; i >= 1; i--) {
        for (int j = i; j <= n; j++) {
            dp[i][j] = max(dp[i + 1][j] + (n - (j - i)) * a[i],
                           dp[i][j - 1] + (n - (j - i)) * a[j]);
        }
    }
    cout << dp[1][n] << endl;
}

HDU 1078 FatMouse and Cheese

大意:

给出一个矩阵,从 ( 0 , 0 ) (0,0) (0,0)点开始,每次只能走到元素比当前大的位置,每次可以横向或者纵向走k步,问最多能走到的位置和的最大值是多少

思路:

记忆化搜索

#include <bits/stdc++.h>

using namespace std;

const int N = 1e2 + 5;
typedef long long LL;
int n, k, a[N][N], dp[N][N];
int f[2][4] = {0, 0, 1, -1, 1, -1, 0, 0};
int dfs(int x, int y) {
    if (dp[x][y]) return dp[x][y];
    int temp = 0;
    for (int i = 1; i <= k; i++) {
        for (int j = 0; j < 4; j++) {
            int xx = x + f[0][j] * i;
            int yy = y + f[1][j] * i;
            if (xx >= 0 && xx < n && yy >= 0 && yy < n && a[xx][yy] > a[x][y]) {
                temp = max(temp, dfs(xx, yy));
            }
        }
    }
    return dp[x][y] = temp + a[x][y];
}

int main() {
    while (cin >> n >> k && (n != -1 && k != -1)) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cin >> a[i][j];
                dp[i][j] = 0;
            }
        }
        cout << dfs(0, 0) << endl;
    }
    return 0;
}

HDU 2859 Phalanx

大意:

给出一个矩阵,要求输出最大的 延左下到右上的对角线对称的 矩阵大小

思路:

n o w [ i ] [ j ] now[i][j] now[i][j]代表以 ( i , j ) (i,j) (i,j)为左上角的长度为k矩阵是否对称,那么它可以由 p r e [ i − 1 ] [ j ] pre[i-1][j] pre[i1][j] p r e [ i ] [ j − 1 ] pre[i][j-1] pre[i][j1]转移过来, p r e [ i ] [ j ] pre[i][j] pre[i][j]代表以 ( i , j ) (i,j) (i,j)为左上角的长度为k-1的矩阵是否对称,所以如果 p r e [ i − 1 ] [ j ] pre[i-1][j] pre[i1][j] p r e [ i ] [ j − 1 ] pre[i][j-1] pre[i][j1]均为true,那么只需要 a [ i ] [ j ] = = a [ i + k − 1 ] [ j + k − 1 ] a[i][j]==a[i+k-1][j+k-1] a[i][j]==a[i+k1][j+k1]即可

#include <bits/stdc++.h>

using namespace std;

const int N = 1e3 + 5;
typedef long long LL;
int n, pre[N][N], now[N][N];
char a[N][N];
int main() {
    while (cin >> n && n != 0) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cin >> a[i][j];
                pre[i][j] = 1;
            }
        }
        int res = 1;
        for (int k = 2; k <= n; k++) {
            int flag = 0;
            for (int i = 0; i <= n - k; i++) {
                for (int j = 0; j <= n - k; j++) {
                    if (a[i][j] == a[i + k - 1][j + k - 1] && pre[i + 1][j] &&
                        pre[i][j + 1])
                        now[i][j] = 1, flag = 1;
                    else
                        now[i][j] = 0;
                }
            }
            if (flag)
                res = k;
            else
                break;
            for (int i = 0; i <= n - k; i++)
                for (int j = 0; j <= n - k; j++) pre[i][j] = now[i][j];
        }
        cout << res << endl;
    }

    return 0;
}

POJ 3616 Milking Time

大意:

m个牛需要挤奶,他们都有一个开始时间结束时间以及价值,对于每只牛,挤完奶需要休息k分钟,最后问能挤多少价值的奶。

思路:

排序后从左到右更新即可

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e6 + 5;
int dp[N], res;
struct node {
    int l, r, v;
} a[N];
int n, m, r;
bool cmp(node a, node b) {
    if (a.l == b.l) return a.r < b.r;
    return a.l < b.l;
}
int main() {
    cin >> n >> m >> r;
    for (int i = 0; i < m; i++) {
        cin >> a[i].l >> a[i].r >> a[i].v;
        dp[i] = a[i].v;
    }
    int res = 0;
    sort(a, a + m, cmp);
    for (int i = 0; i < m; i++) {
        dp[i] = a[i].v;
    }
    for (int i = 0; i < m; i++) {
        for (int j = i + 1; j < m; j++) {
            if (a[j].l >= a[i].r + r) dp[j] = max(dp[j], dp[i] + a[j].v);
        }
        res = max(res, dp[i]);
    }
    cout << res << endl;
}

POJ 3666 Making the Grade

大意:

给出n个数,将这个数列修改为不增序列或者不减序列,需要的最小代价是多少(每次修改的代价为修改前后的差的绝对值)

思路:

贪心的想法是每次修改必然修改到原数列中存在的数,因为不这样修改必然会修改多了。

然后先考虑修改到不减序列,那么可以先将a数组排序,得到b数组,然后 d p [ i ] [ j ] dp[i][j] dp[i][j]代表将第i个数修改到 b [ j ] b[j] b[j]的代价最小值,那么这个最小值可以从 m i n ( d p [ i − 1 ] [ 1.... j ] ) min(dp[i-1][1....j]) min(dp[i1][1....j])转移过来,但是这样复杂度很高,是 O ( n 3 ) O(n^3) O(n3)的,怎么优化呢,注意到每次更新j时也会更新 m i n ( d p [ i − 1 ] [ 1.... j ] ) min(dp[i-1][1....j]) min(dp[i1][1....j]),所以直接在更新 d p [ i ] [ j ] dp[i][j] dp[i][j]的时候更新 m i n ( d p [ i ] [ 1.... j ] ) min(dp[i][1....j]) min(dp[i][1....j])即可,

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 2e3 + 5;
typedef long long LL;

int n, a[N], b[N], dp[N][N];
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        b[i] = a[i];
    }
    sort(b, b + n);
    for (int i = 0; i < n; i++) dp[0][i] = min(dp[0][i], abs(a[0] - b[i]));
    for (int i = 1; i < n; i++) {
        int temp = dp[i - 1][0];
        for (int j = 0; j < n; j++) {
            temp = min(temp, dp[i - 1][j]);
            dp[i][j] = temp + abs(a[i] - b[j]);
        }
    }
    int res = dp[n - 1][0];
    for (int i = 0; i < n; i++) {
        res = min(res, dp[n - 1][i]);
    }
    cout << res << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值