【cqbzoj1510】 遇见 滚动数组 dp 解题报告

这是一篇关于如何应用滚动数组解决动态规划问题的解题报告。文章详细介绍了题目背景,包括燕姿过桥的情景,以及如何通过计算桥上数字组合来找到最接近给定目标值的数字。作者提出了一个状态转移方程,并提供了样例输入和输出,以及最优解的示例。最后,文章提到了具体的代码实现,但并未展示具体内容。
摘要由CSDN通过智能技术生成

遇见

时间限制: 1 Sec 内存限制: 64 MB

题目描述

燕姿在桥的这一端,而xx在桥的另一端。这座桥非常特殊,桥面是由2N-1个方格组成的,每个方格里写有一个数码Ai(-50<=Ai<=50)。如下是N=4时的情况。可以认为燕姿从最下面出发。每一次,她可以向上跳到与自己所在方格相临的其中一个方格内(例如在最下面的7中,可以跳到上一行的10和8中)。当燕姿跳到最顶端的方格后,她就不能再移动了。(在未到顶端前,不允许跳到表格外。)每在一格内,都要把格内的数字写下来。 但是,仅仅到达顶端是不够的。桥会向对岸的xx询问一个数字k,燕姿到达顶端后,拿出写下来的数字,可以在任意两个数字间添加“+”或“-”号,使得计算的结果m最接近k。经过桥的判断,如果对于桥上的方格m是最接近k的数字,那么燕姿就可以通过桥和xx相遇,否则……… (为了让燕姿能更容易地通过,xx给出的数字总是0)你的任务,就是帮助燕姿找出这个最接近k的m.

输入

输入的第一行是N(1<=N<=30),接下来2N-1行给出了表格中每行的每个方格中的数字,第i+1行的第j个数字对应于表格中第i行的第j个数字。文件中第二行的数字表示的是表格顶端的方格中的数字。所有的数字都是整数,同一行相邻的两个数字间用空格符隔开。

输出

输出只有一行,是你所求出的最接近零的计算结果的绝对值

样例输入

4
2
3 1
-3 5 7
6 10 -2 20
-7 -5 -8
10 8
7

样例输出

0

提示

最优解7+8+(-5)+(-2)-5-1-2=0
或7+10+(-7)-6+(-3)-3+2=0
或7+10+(-5)-10-5+1+2=0
或+10+(-5)+(-2)-5-3-2=0

解题报告

一道dp题
设inp[i][j]为桥上i行j列的数字
f(i,j,k) 为到第i行第j列所有数字组成(注意此处加减任意)k的可行性(false/true)
那么有大约形如

f(i,j,k)=f(i1,j1,kinp[i][j])||f(i1,j1,k+inp[i][j])||f(i1,j,kinp[i][j])||f(i1,j,k+inp[i][j])

的递推式
是不是感觉和背包比较像呢~
在不同情况下有轻微变更 (由于作者比较懒)
但是f数组过于巨大肯定是开不下的,
发现递推的一维最多用到i和i-1,
于是可以使用滚动数组节约空间
那么变成
f((i&1),j,k)=f(!(i&1),j1,kinp[i][j])||f(!(i&1),j1,k+inp[i][j])||f(!(i&1),j,kinp[i][j])||f(!(i&1),j,k+inp[i][j])

每次把f[i&1]memset清空就可以了

接下来是具体的代码实现

#include<cstring>
#include<cstdio>
const int MAXN=35;
inline int min(const int &a,const int &b)
{return a>b?b:a;}
int inp[MAXN<<1][MAXN];
bool f[2][MAXN][3100];
int main(){
    int n;
    scanf("%d",&n);
    scanf("%d",&inp[1][1]);
    f[1][1][1500+inp[1][1]]=
    f[1][1][1500-inp[1][1]]=true;
    for(int i=2;i<=n;i++){
        memset(f[i&1],0,sizeof f[i&1]);
        for(int j=1;j<=i;j++){
            scanf("%d",&inp[i][j]);
            for(int k=50;k<=2950;k++)
                f[i&1][j][k]=
                f[!(i&1)][j][k+inp[i][j]]||
                f[!(i&1)][j][k-inp[i][j]]||
                f[!(i&1)][j-1][k+inp[i][j]]||
                f[!(i&1)][j-1][k-inp[i][j]];
        }
    }
    int t;
    for(int i=n-1;i>=1;i--){
        memset(f[i&1],0,sizeof f[i&1]);
        for(int j=1;j<=i;j++){
            scanf("%d",&t);
            for(int k=50;k<=3000;k++)
                f[i&1][j][k]=
                f[!(i&1)][j][k+t]||
                f[!(i&1)][j][k-t]||
                f[!(i&1)][j+1][k+t]||
                f[!(i&1)][j+1][k-t];
            for(int k=0;k<50 ;k++)
                f[i&1][j][k]|=
                f[!(i&1)][j][k+t]||
                f[!(i&1)][j+1][k+t];
        }
    }
    int u,d;
    for(u=1500;u<=3000;u++)
        if(f[1][1][u])break;
    for(d=1500;d>=0;d--)
        if(f[1][1][d])break;
    int ans=min(u-1500,1500-d);
    printf("%d\n",ans);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值