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

来源:互联网 发布:mysql 按月查询 编辑:程序博客网 时间:2024/06/08 11:58

遇见

时间限制: 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);}
1 0
原创粉丝点击