您的位置:首页 > 其它

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

2014-08-24 20:03 399 查看
题意是给定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
, maxv
);
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: