zoukankan      html  css  js  c++  java
  • kuangbin带你飞 专题十二 基础DP

    HDU 1024 Max Sum Plus Plus

    大意:

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

    思路:

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

        (dp[i][j] = max( dp[i][j-1] + a[j] , dp[i-1][k] + a[j ]) i-1<=k<=j-1)

    这个方程的含义是:

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

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

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

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

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

    所以我们要将其优化:

    首先我们发现$ 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] (这个庞大的二维数组了 只需要开一个 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个种类的正方体的六种摆放方式都存下来,然后(dp[i])代表以第i个正方体为顶的塔的高度,那么(n^2)去枚举,符合严格小于的条件就更新即可

    #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
    ",t,res); 
        }
        return 0;
    }
    

    HDU 1074 Doing Homework

    大意:

    给出n((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
    ", 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
    ",dp[0][5]);
    	}
    }
    
    

    HDU 1260 Tickets

    大意:

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

    思路:

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

    #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
    ", hh - 4, mm, ss);
            else printf("%02d:%02d:%02d am
    ", 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个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。

    思路:

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

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

    初始将dp数组赋值为-1,(dp[0][m*20]=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))位置落下,下落速度恒定为1,当落到一个平台时可以向左也可以向右走,速度也是1,走到边缘时继续下落,每次下落距离不能超过k米,现在给出n个平台的左右边缘位置和高度,问小球最快多久能落到地面

    思路:

    首先将平台按照高度排一下序,然后计算一下小球第一次碰到的是哪个平台,然后从这个平台开始更新dp数组。(dp[i][0])代表到达第i个平台的左边缘最快时间是多少,(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),k为第几次取,问最大价值和是多少

    思路

    区间dp,(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))点开始,每次只能走到元素比当前大的位置,每次可以横向或者纵向走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

    大意:

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

    思路:

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

    #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数组,然后(dp[i][j])代表将第i个数修改到(b[j])的代价最小值,那么这个最小值可以从(min(dp[i-1][1....j]))转移过来,但是这样复杂度很高,是(O(n^3))的,怎么优化呢,注意到每次更新j时也会更新(min(dp[i-1][1....j])),所以直接在更新(dp[i][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;
    }
    
  • 相关阅读:
    Timer Pattern
    la négation
    expression de la fréquence
    .NET 索引器
    JQuery.Gantt(甘特图) 开发指南
    .NET 预处理器指令
    .NET 数据类型之匿名类型(var)
    .NET base与this
    .NET using关键字
    .NET 基础语句
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14215270.html
Copyright © 2011-2022 走看看