zoj 3798 Abs Problem(数学:推理+滚动数组)

题意是给定n个数,定义一种运算为每次取当前数和下一个数的差的绝对值作为下一次运算的当前数

输出最小值,最大值以及对应的路径


感觉很犀利的一道题,但是比赛的时候AC率特别高...

我用了一个多小时才写出来,刚开始就想出来了递推步骤

受这几天刷的题的影响吧,第一反应是打表保存结果

我用vector保存,结果把我的电脑都跑死机了尴尬

开机后就想到了用一维数组即滚动数组优化来写

先说下分析过程:

令minv[i], maxv[i]分别保存i对应的最小值和最大值

不难想到:

maxv[i] = i - minv[i-1]
minv[i] = i -maxv[i-1]


那么这种思路对不对呢?观察下面的例子:

例如i==2时

minv[2] : 1 2

maxv[2]: 1 2

i == 3时

minv[3]: 1 2 3(错误!正确排列应该为:1 3 2)

maxv[3]: 1 2 3

所以求minv[i]时我们还要比较以下两种排列的大小:

minv[i-1]的前 i-1 项排列 + i

minv[i-1]的前 i-2 项排列 + i-1 + i

而对于maxv[i]则不需要额外的判定,只需在minv[i-1]的排列后+i即可

前8项的排列如下:




观察规律还可以发现最小值非0即1(与n*(n+1)/2 的奇偶性有关)

同样最大值非i即i-1(与n*(n-1)/2的奇偶性有关)

唯一的难点就在于打印路径了

我是用滚动数组的方法做的

不太好解释,直接看代码吧

代码如下:

#include <bits/stdc++.h>
#define MAXN 60010
#define LL long long
using namespace std;

int n;
int minv[MAXN], maxv[MAXN];
vector<LL> res_min, res_max;

void init(int n) {
    res_min.clear();
    res_max.clear();

    minv[1] = maxv[1] = 1;
    res_min.push_back(1);
    res_max.push_back(1);

    for(LL i=2; i<=n; ++i) {
        LL tmp = i*(i+1)/2;
        if(tmp % 2) minv[i] = 1;
        else minv[i] = 0;
        tmp -= i;
        if(tmp % 2) maxv[i] = i-1;
        else maxv[i] = i;

        //printf("maxv[%d] = %d\n", i, maxv[i]);

        if(i%2) {
            if(i-maxv[i-1] > 1) {
                res_min[i-2] = i;
                res_min.push_back(i-1);
            } else res_min.push_back(i);
            res_max.push_back(i);
        }

        else {
            if(i-maxv[i-1] > 1) {
                res_max[i-2] = i;
                res_max.push_back(i-1);
            } else res_max.push_back(i);
            res_min.push_back(i);
        }

    }

}

int main(void) {
    while(scanf("%d", &n) != EOF) {
        init(n);
        printf("%d %d\n", minv[n], maxv[n]);
        if(n%2)//i为奇数时,res_minv[i]中保存的是最小排列,反之res_maxv[i]中保存的是最小排列
            for(int i=0; i<n; ++i)
                printf("%lld ", res_min[i]);
            puts("");
            for(int i=0; i<n; ++i)
                printf("%lld ", res_max[i]);
            puts("");
        } else {
            for(int i=0; i<n; ++i)
                printf("%lld ", res_max[i]);
            puts("");
            for(int i=0; i<n; ++i)
                printf("%lld ", res_min[i]);
            puts("");

        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值