CF 518 E Arthur and Questions(贪心,模拟乱搞)

题目:LINK
题意:给你一个数列a,从左到右每k个连续的数字求和,每个连续区间的和组成一个新的数列,要求这个新的数列是严格递增的。原始数列中的某些数字是'?',要你确定这些数字来满足上面的条件,无法满足输出"Incorrect sequence" ,满足的话,输出最优解(确定出来的原始数列的绝对值求和最小).
可以发现满足上面的条件,
a1  +  a2 ...  +  ak < a2  +  a3  +  ...  +  ak + 1 < a3  +  a4  +  ...  +  ak + 2<..........
a[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		  : [email protected]
 	Created Time  : 2015-02-25 02:45:54
	Problem       : CF_518_E.cpp
************************************************ */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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; 
}


你可能感兴趣的:(Codeforces,模拟,贪心)