51NOD中的矩阵取数问题(1083,1084,1411)——动态规划,插头dp

唉,dp还是不行,多加努力。

基准时间限制:1 秒 空间限制:131072 KB 分值: 5  难度:1级算法题
 收藏
 关注
一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下向右走,求能够获得的最大价值。
例如:3 * 3的方格。

1 3 3
2 1 3
2 2 1

能够获得的最大价值为:11。
Input
第1行:N,N为矩阵的大小。(2 <= N <= 500)
第2 - N + 1行:每行N个数,中间用空格隔开,对应格子中奖励的价值。(1 <= N[i] <= 10000)
Output
输出能够获得的最大价值。
Input示例
3
1 3 3
2 1 3
2 2 1
Output示例
11


入门级dp,直接记录状态转移即可。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
using namespace std;

const int INF=1000000007;
const int MAXN =1010;
const int MAXM=1000010;
const int MODE=1000000007;


int dp[MAXN][MAXN];

int a[MAXN][MAXN];

int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            dp[i][j]=-INF;
    }
    dp[1][1]=a[1][1];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            dp[i][j]=max(dp[i][j],max(dp[i-1][j],dp[i][j-1])+a[i][j]);
        }
    }
    printf("%d\n",dp[n][n]);
}




基准时间限制:2 秒 空间限制:131072 KB 分值: 80  难度:5级算法题
 收藏
 关注
一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。
 
例如:3 * 3的方格。

1 3 3
2 1 3
2 2 1

能够获得的最大价值为:17。1 -> 3 -> 3 -> 3 -> 1 -> 2 -> 2 -> 2 -> 1。其中起点和终点的奖励只计算1次。
Input
第1行:2个数M N,中间用空格分隔,为矩阵的大小。(2 <= M, N <= 200)
第2 - N + 1行:每行M个数,中间用空格隔开,对应格子中奖励的价值。(1 <= A[i,j] <= 10000)
Output
输出能够获得的最大价值。
Input示例
3 3
1 3 3
2 1 3
2 2 1
Output示例
17

一个从左上到右下,另一个右下到左上。相当于是有两个同时从左上到右下。走到相同处就只记录一次。

如果用dp[x1][y1][x2][y2]记录会超内存。

但是因为是同时前进,dp[x1][x2][k]记录即可,有了前进步数就能知道y坐标。

具体的看代码。

注意是先读m再读n


#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
using namespace std;

const int INF=1000000007;
const int MAXN =210;
const int MAXM=1000010;
const int MODE=1000000007;

int a[MAXN][MAXN];
int dp[MAXN][MAXN][MAXN*2];
int main(){
    int n,m;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int k=2;k<=(n+m+1);k++){
        int t=min(n,k);
        for(int x1=1;x1<=t;x1++){
            for(int x2=1;x2<=t;x2++){
                int y1=k-x1;
                int y2=k-x2;
                int ans=max(max(dp[x1-1][x2][k-1],dp[x1][x2-1][k-1]),max(dp[x1-1][x2-1][k-1],dp[x1][x2][k-1]));
                if(x1==x2)
                    ans+=a[x1][y1];
                else
                    ans+=a[x1][y1]+a[x2][y2];
                if(ans>dp[x1][x2][k]){
                    dp[x1][x2][k]=ans;
                }
            }
        }
    }
    printf("%d\n",dp[n][n][n+m+1]);
}

基准时间限制:2 秒 空间限制:131072 KB 分值: 320  难度:7级算法题
 收藏
 关注
给定一个m行n列的矩阵,你可以从任意位置开始取数,到达任意位置都可以结束,每次可以走到的数是当前这个数上下左右的邻居之一,唯一的限制是每个位置只能经过一次,也就是说你的路径不自交。所经过的数的总作为你的得分,求最大的得分。
Input
第一行两个整数m, n (0 < m, n < 10),表示矩阵的行数和列数。
后面m行,每行n个整数表示矩阵里的数,整数范围[-10000000, +10000000]。
Output
一个整数表示最大得分。
Input示例
3 3
-1 2 -3
-4 5 -6
-10 3 2
Output示例
12
插头dp,还在学习中。学会了补代码。

把结题报告贴上供大家一起学习。


状态表示,用蓝色的边界线表示状态。
 
每个格子可能是如下几种情况,具体选择,取决于边界线上每个格子“对外”的情况。 

边界线对外可以是入(IN),出(OUT),和无(NONE)。注意“无”可能是左下角或者左上角的情况,即消耗掉一个IN,一个OUT。

如何表示边界线(蓝线)上的状态?
可以用0,1,2表示一个格子的状态,这样所有格子可以用二进制压缩,每个格子占2bit。每决定一个新格子,考虑它左面和上面格子的状态,来枚举当前格子的状态。注意点:
(1) 蓝色边界线上#IN = #OUT,即IN, OUT的总数相等
(2) 连接一个格子的时候,可能会修改之后的IN, OUT (反向)
(3) NONE不一定是没经过(如前所述),可以是抵消一对IN和 OUT。

状态表示dp[x][y][state] 表示当前在(x,y)时,IN-OUT状态(二进制整数)state的最大的分。
dp[x][y]与dp[x][y-1][state]有关。

具体插头dp细节可以参看国家集训队论文 <基于连通性状态压缩的动态规划问题>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值