题目:LINK
题意:给你一个数列a,从左到右每k个连续的数字求和,每个连续区间的和组成一个新的数列,要求这个新的数列是严格递增的。原始数列中的某些数字是'?',要你确定这些数字来满足上面的条件,无法满足输出"Incorrect sequence" ,满足的话,输出最优解(确定出来的原始数列的绝对值求和最小).
可以发现满足上面的条件,
a1 + a2 ... + ak < a2 + a3 + ... + ak + 1 < a3 + a4 + ... + ak + 2<..........
即 a[1]<a [k+1]<a[2*k+1].......
a[2] < a[k+2] < a[2*k+2]....
a[i] < a[k+i] < a[2*k+2]...
只要满足这个规律构造出来的一定是正确的结果,我们可以知道上面的式子一共会有k个,即形成k条链,只要每条链满足严格递增就可以了。
为了满足绝对值求和最小,在确定?的时候,如果当前已有的数字是正数,那么我们要向右去填'?',不断+1。相反当前已有的数字是负数,我们要向左去填,不断-1.正负数中间的要从0开始绝对值不断增加,来使得求和最小。
为了方便处理,我们可以在这个数列的开始部分加上k个递增的很小的负数,在数列的末尾加上k个递增的很大的正数。
题意:给你一个数列a,从左到右每k个连续的数字求和,每个连续区间的和组成一个新的数列,要求这个新的数列是严格递增的。原始数列中的某些数字是'?',要你确定这些数字来满足上面的条件,无法满足输出"Incorrect sequence" ,满足的话,输出最优解(确定出来的原始数列的绝对值求和最小).
可以发现满足上面的条件,
a1 + a2 ... + ak < a2 + a3 + ... + ak + 1 < a3 + a4 + ... + ak + 2<..........
即 a[1]<a [k+1]<a[2*k+1].......
a[2] < a[k+2] < a[2*k+2]....
a[i] < a[k+i] < a[2*k+2]...
只要满足这个规律构造出来的一定是正确的结果,我们可以知道上面的式子一共会有k个,即形成k条链,只要每条链满足严格递增就可以了。
为了满足绝对值求和最小,在确定?的时候,如果当前已有的数字是正数,那么我们要向右去填'?',不断+1。相反当前已有的数字是负数,我们要向左去填,不断-1.正负数中间的要从0开始绝对值不断增加,来使得求和最小。
为了方便处理,我们可以在这个数列的开始部分加上k个递增的很小的负数,在数列的末尾加上k个递增的很大的正数。
详见代码注释
/* ***********************************************
Author : Napoleon
Mail : tyfdream@163.com
Created Time : 2015-02-25 02:45:54
Problem : CF_518_E.cpp
************************************************ */
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define INF 1010000000
//typedef __int64 LL;
#define N 111111
int n, k;
int mark[N<<2], num[4*N], add;
char str[11];
int getnum(char str[]) {
int ret = 0, neg = -1, i = 1;
if(str[0] != '-') neg = 1, i = 0;
for(; str[i]; i++) {
ret = ret * 10 + str[i] - '0';
}
ret *= neg;
return ret;
}
void init() {
for(int i = 1;i <= add; i++) {
num[i] = -INF +i;
num[add + n + i] = INF + i;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
scanf("%d%d", &n, &k);
add = k;
for(int i = 1;i <= n; i++) {
scanf("%s", str);
if(str[0] == '?') mark[i+ add] = 1;
else num[i + add] = getnum(str);
}
init();
int flag = 0;
for(int i = 1; i <= k; i++) {//一共k次处理(k个链),每个链条递增且绝对值之和最小
if(flag) break;
int negid = -1, posid = -1;
int lastval = num[i];
for(int j = i+k; j <= n + 2*add; j+=k) {//先检查不是'?'的序列是不是递增
if(mark[j]) continue;
if(num[j] <= lastval) {
flag =1 ; break;
}
lastval = num[j];
}
if(flag) break;
for(int j = i; j <= n + 2*add; j += k) {//找到最后一个负数的位置
if(posid == -1 && !mark[j] && num[j] >= 0) {
posid = j; break;
}
}
for(int j = posid; j >= 1; j -= k) {//找到第一个正数的位置
if(negid == -1 && !mark[j] && num[j] < 0) {
negid = j; break;
}
}
//have found negtive and postive num id
for(int j = posid+k; j <= n + 2*add; j += k) {//从左至右填正数的递增部分
int lastid = j - k;
if(!mark[j]) {
if(num[lastid] >= num[j]) flag = 1;
continue;
}
if(flag) break;
mark[j] = 0;
num[j] = num[lastid] + 1;
}
if(flag) break;
for(int j = negid-k; j >= 1; j -= k) {//从右至左填负数的递减部分
int lastid = j + k;
if(!mark[j]) {
if(num[j] >= num[lastid]) flag = 1;
continue;
}
if(flag) break;
mark[j] = 0;
num[j] = num[lastid] -1;
}
int midlen = (posid - negid)/k -1;
if(midlen > num[posid] - num[negid]) {//填正负数中间的部分,先判断
flag = 1; break;
}
int left = -((midlen - 1) /2);
int right = (midlen)/2;
if(num[posid] <= right) {//根据正负数的范围调整中间要填的数字范围
int dis = right - num[posid] + 1;
right -= dis; left -=dis;
}
if(num[negid] >= left) {
int dis = num[negid] - left + 1;
right += dis; left += dis;
}
for(int j = negid + k; j < posid; j += k) {
num[j] = left; left += 1;
}
}
if(flag) puts("Incorrect sequence");
else {
for(int i = k+1; i <= k+ n; i++) printf("%d ", num[i]);
puts("");
}
return 0;
}