poj 2454(贪心+随机化算法)

本文探讨了一种算法,用于解决将一个整数序列划分为三个子序列的问题,要求至少有两个子序列的和大于500倍的序列长度。通过贪心策略和随机化算法实现了这一目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

已知:给定一长度为3K(1≤K≤60)的整数序列S,将其划分为等长(各为K)的三个子序列S1、S2和S3,要求其中至少两个子序列各自的加和大于500*K

求:满足上述条件的一个划分(所给输入必有解)


输入:第1行为K的值;第2行至第3K+1行为序列S的元素值

输出:满足条件的序号划分


Sample Input:

2

510

500

500

500

670

400

310


Sample Output:

1

2

3

6

5

4


思路:

首先,只要S1、S2和S3中的两个其加和大于500*K即可,因此我们采用贪心策略,对输入序列S进行降序排序,将排序后的前2K个元素划分给S1和S2(假设最终满足条件的就是S1和S2),不再考虑S3

设S' = (S1, S2),现考虑采用随机化算法

第一种策略:循环地随机重排S‘中的所有元素,进行条件检测,若满足则退出循环输出结果;否则继续循环。这种策略会造成大量浪费,因为会出现如下情况:随机重排一次后,S1和S2各自包含的元素没有发生变化,导致此次重排没有意义。此外对整个S’进行重排也很耗时

第二种策略:每次分别在S1和S2中各随机选取一个元素进行互换,进行条件检测。这种策略能保证绝大部分的重排是有效的(除非本次随机选取的两个元素恰好是上次随机选取的那两个元素)


代码(第二种策略)

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <limits.h>
int a[200][2];
int k;
void sort(){
	int n = 3*k;
	for(int i = 0; i < n-1; i++){
		int max = i;
		for(int j = i+1; j < n; j++){
			if(a[j][0] > a[max][0])
				max = j;
		}
		if(max != i){
			int tmp1 = a[max][0];
			int tmp2 = a[max][1];
			a[max][0] = a[i][0];
			a[max][1] = a[i][1];
			a[i][0] = tmp1;
			a[i][1] = tmp2;
		}
	}
}

int main(){
	scanf("%d", &k);
	int floor = 500*k;
	for(int i = 0; i < 3*k; i++){
		scanf("%d", &a[i][0]);
		a[i][1] = i;
	}
	sort();
	int sum1 = 0;
	int sum2 = 0;
	for(int i = 0; i < k; i++){
		sum1 += a[i][0];
		sum2 += a[i+k][0];
	}
	srand((unsigned int)time(0));
	while(!(sum1 > floor && sum2 > floor)){
		int offset1 = rand()%k;
		int offset2 = rand()%k;
		sum1 = sum1-a[offset1][0]+a[k+offset2][0];  //注意先更新加和再互换元素
		sum2 = sum2-a[k+offset2][0]+a[offset1][0];
		int tmp1 = a[offset1][0];
		int tmp2 = a[offset1][1];
		a[offset1][0] = a[k+offset2][0];
		a[offset1][1] = a[k+offset2][1];
		a[k+offset2][0] = tmp1;
		a[k+offset2][1] = tmp2;
	}
	for(int i = 0; i < 3*k; i++){
		printf("%d\n", a[i][1]+1);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值