尺取法。。

尺取法:反复推进区间的开头和末尾,来求满足条件的最小区间的方法被称为尺取法。

一般区间有连续或者单调等特点。

例题:

poj 2566

求任意子序列和的绝对值与所给值相差最小的区间

求出前N项的和构成新数组,从小到大排序,再用尺取法做。。。

这题恶心,,,不想多说

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef pair<int,int> P;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f;
int n,m,k;
P p[maxn];

int get_sum(int &l,int &r,int &from,int &to,int &result,int k) {
	if(l>=r) return INT_MIN;
	int sum = p[r].first - p[l].first;
	if(abs(sum-k)<abs(result-k)) {
		from = min(p[l].second,p[r].second);
		to = max(p[l].second,p[r].second);
		result = sum;
	}
	return sum;
}

int main() {
	while(~scanf("%d%d",&n,&m)) {
		if(!n&&!m) return 0;
		p[0] = make_pair(0,0);
		for(int i=1; i<=n; i++) {
			scanf("%d",&p[i].first);
			p[i].first+=p[i-1].first;
			p[i].second = i;
		}
		sort(p,p+n+1);
		while(m--) {
			scanf("%d",&k);
			int result = 0x80808080;
			int sum = 0x80808080;
			int from;
			int to;
			int l=0,r=0;
			for(;;) {

				while(r<n&&sum<k) {
					sum = get_sum(l,++r,from,to,result,k);
					
				}

				if(sum<k) break;//子序列和的绝对值最大值比k小
				sum = get_sum(++l,r,from,to,result,k);
			}
			printf("%d %d %d\n",result,from+1,to);
		}
	}
}

谁能告诉我0x80808080,INT_MN,0x3f3f3f到底是些什么东西!!。。


poj 2739

题意:给定一个数n,求任意连续素数序列之和与n相等的序列总数。

如 53 has two representations 5 + 7 + 11 + 13 + 17 and 53(素数序列必须连续)

先素数打个表。。。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e4+10;
int prime[maxn];
bool is_prime[maxn];
int p = 0;
void sieve(){
	
	for(int i=0;i<=maxn;i++) is_prime[i] = true;
	is_prime[0] = is_prime[1] = false;
	for(int i=2;i<=maxn;i++){
		if(is_prime[i]){
			prime[p++] = i;
			for(int j=2*i;j<=maxn;j+=i) is_prime[j] = false;
		}
	}
}
int main(){
	int n;
	sieve();
	while(~scanf("%d",&n)){
		if(!n) return 0;
		int res = 0;
		int s = 0,t = 0,sum = 0;
		for(;;){
			if(sum==n) res++;
			while(t<p&&sum<n){
				sum+=prime[t++];
				if(sum==n) res++;
			}
			if(sum<n) break;
			sum-=prime[s++];
		}
		printf("%d\n",res);
	}
}


poj 2100

题意:给定一个数n(挺大的),求任意连续整数序列的平方的和等于n的这些序列。

如:

input   2030

output  

2(表示有两个序列可以等于2030)
4 21 22 23 24(第一个数为该序列的整数个数,后面为每个整数)
3 25 26 27
21^2+22^2+23^2+24^2 = 2030

题目的输出很烦,看了网上的,用到了vector。。还是不知道使用这些数据结构啊。。。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef  long long ll;
const int maxn = 1e6;
vector<pair<ll,ll> > ans;
int main() {
	ll n;
	while(~scanf("%lld",&n)){
		ll s = 1;
		ll t = 1;
		ll sum = 0;
		while(s*s<=n){
			while(t*t<=n&&sum<n){
				sum+=t*t;
				t++;
			}
			if(sum==n){
				ans.push_back(make_pair(s,t));
				//printf("%lld %lld\n",s,t);
			}
			sum-=s*s;
			s++;
		}
		ll num = ans.size();
		printf("%lld\n",num);
		for(int i=0;i<num;i++){
			printf("%lld",ans[i].second-ans[i].first);
			for(int j=ans[i].first;j<ans[i].second;j++){
				printf(" %lld",j);
			}
			printf("\n");
		}
	}
}

总结:尺取法总的来说比较容易,只要知道了题目要用到尺取法,应该就可以做出来了,剩下的就是细节实现问题。

三个题目,大致框架都是一样。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值