《挑战程序设计竞赛》3.2.1 常用技巧-尺取法 POJ3061 3320 2566 2739 2100(1)
2015-12-31 22:58
369 查看
POJ3061
http://poj.org/problem?id=3061题意
给定长度为n的整数数列以及整数S,求出总和不小于S的连续子序列的长度的最小值,如果解 不存在,输出0.思路
如果用二分法:先求出sum[i],从第1个数到第i个数的区间和,每次固定一个开始查找的起点sum[i], 然后采用二分查找找到 sum[i] + S 的位置,区间长度即为(末位置-(起始位置-1)),用ans保存过程中区间的最小值。时间复杂度 O(nlogn)。
但如果用尺取法会将复杂度大大降低:
反复地推进区间的开头和末尾,来求满足条件的最小区间的方法称为尺取法。
主要思想为:当a1, a2 , a3 满足和>=S,得到一个区间长度3,那么去掉开头a1, 剩下 a2,a3,判断是否满足>=S,如果满足,那么区间长度更新,如果不满足,那么尾部向后拓展,判断a2,a3,a4是否满足条件。重复这样的操作。
时间复杂度 O(n)。
对尺取法的深入理解:
当一个区间满足条件时,那么去掉区间开头第一个数,得到新区间,判断新区间是否满足条件,如果不满足条件,那么区间末尾向后扩展,直到满足条件为之,这样就得到了许多满足条件的区间,再根据题意要求什么,就可以在这些区间中进行选择,比如区间最长,区间最短什么的。
代码
[code]Source Code Problem: 3061 User: liangrx06 Memory: 632K Time: 63MS Language: C++ Result: Accepted Source Code #include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int N = 1e5; int main(void) { int t, n, s; int a ; cin >> t; while (t--) { cin >> n >> s; for (int i = 0; i < n; i ++) scanf("%d", &a[i]); int ans = n+1; int l = 0, r = 0, sum = 0; while (true) { while (sum < s && r < n) sum += a[r++]; if (sum < s) break; ans = min(ans, r-l); sum -= a[l++]; } printf("%d\n", ans%(n+1)); } return 0; }
POJ3320
http://poj.org/problem?id=3320题意
某人读一本书,要看完所有的知识点,这本书共有P页,第i页恰好有一个知识点ai,(每一个知识点都有一个整数编号)。全书同一个知识点可能会被提到多次,他希望阅读其中一些连续的页把所有知识点都读到,给定每页所读到的知识点,求最少的阅读页数。思路
和上一题一样,也是尺取法的应用。假设从某一页s开始阅读,为了覆盖所有的知识点读到t页,这样的话如果从s+1开始阅读,那么必须读到t’>=t位置,故可以用尺取法。
预处理时要用set来统计所有知识点。
尺取法循环过程中,对每个知识点用map来统计次数,增加后一项要把对应的知识点的编号次数+1,取出前一项要把对应的知识点的编号次数-1。用cnt统计覆盖的知识点数目,知识点次数由0变为1时cnt++,反之则cnt–。
代码
[code]Source Code Problem: 3320 User: liangrx06 Memory: 5116K Time: 532MS Language: C++ Result: Accepted Source Code #include <iostream> #include <cstdio> #include <algorithm> #include <set> #include <map> using namespace std; const int N = 1e6; int main(void) { int n; int a ; set <int> ideas; map <int, int> num; cin >> n; for (int i = 0; i < n; i ++) { scanf("%d", &a[i]); ideas.insert(a[i]); num[a[i]] = 0; } int s = ideas.size(); int l = 0, r = 0, cnt = 0; int ans = n; while (true) { while (r < n && cnt < s) { if (num[a[r]] == 0) cnt++; num[a[r++]] ++; } if (cnt < s) break; ans = min(ans, r-l); if (num[a[l]] == 1) cnt--; num[a[l++]] --; } printf("%d\n", ans); return 0; }
POJ2739
http://poj.org/problem?id=2739题意
给定一个10000以内的数字,判断这个数字是否可以由几个连续的素数(例如:2,3,5,7…)相加得到,并且给出这个数可以有几组这样的解。输入:每行一个数字,0为退出
输出:每行一个数字,对应输入的每个数字的解的组数。
思路
预先用素数筛法求出10000以内的素数,按顺序存储。然后用尺取法求解。代码
[code]Source Code Problem: 2739 User: liangrx06 Memory: 284K Time: 0MS Language: C++ Result: Accepted Source Code #include <iostream> #include <cstdio> #include <cmath> using namespace std; const int N = 10000; bool isPrime[N+1]; int prime[N+1]; void initPrime() { fill(isPrime, isPrime+N, true); fill(prime, prime+N, N+1); isPrime[1] = false; int cnt = 0; for (int i = 2; i <= N; i ++) { if (isPrime[i]) { prime[cnt++] = i; if (i > (int)sqrt((double)N)) continue; for (int j = i*i; j <= N; j += i) { isPrime[j] = false; } } } } int main(void) { int n; initPrime(); while (cin >> n && n) { int ans = 0; int l = 0, r = 0, sum = 0; while (true) { while (sum < n && prime[r] <= n) sum += prime[r++]; if (sum < n) break; if (sum == n) ans++; sum -= prime[l++]; } printf("%d\n", ans); } return 0; }
POJ2100
http://poj.org/problem?id=2100题意
是否存在一段连续的数, 平方相加等于n, 求出方案数并输出方案.思路
连续子序列的性质通常用尺取法比较合适。另外这个题其实保存序列后不需要重新排序,顺序输出即可。代码
[code]Source Code Problem: 2739 User: liangrx06 Memory: 284K Time: 0MS Language: C++ Result: Accepted Source Code #include <iostream> #include <cstdio> #include <cmath> using namespace std; const int N = 10000; bool isPrime[N+1]; int prime[N+1]; void initPrime() { fill(isPrime, isPrime+N, true); fill(prime, prime+N, N+1); isPrime[1] = false; int cnt = 0; for (int i = 2; i <= N; i ++) { if (isPrime[i]) { prime[cnt++] = i; if (i > (int)sqrt((double)N)) continue; for (int j = i*i; j <= N; j += i) { isPrime[j] = false; } } } } int main(void) { int n; initPrime(); while (cin >> n && n) { int ans = 0; int l = 0, r = 0, sum = 0; while (true) { while (sum < n && prime[r] <= n) sum += prime[r++]; if (sum < n) break; if (sum == n) ans++; sum -= prime[l++]; } printf("%d\n", ans); } return 0; }
相关文章推荐
- 记2015
- Swift语言问题记录
- ScrollView嵌套ListView方法汇总
- gethostbyname()和gethostbyaddr()函数说明
- leetcode笔记:Sqrt(x)
- 2015年广工操作系统实验(要求实现图形化界面)
- 用获得送走2015——生产实习总结
- 《挑战程序设计竞赛》3.1.5 二分搜索-其它 POJ1759 3484
- 8.zabbix学习笔记:zabbix实现网络发现
- ContentProvider
- 《挑战程序设计竞赛》3.1.4 二分搜索-最小化第k大的值 POJ2010 3662(2)
- discuz mysqli_connect() 不支持 advice_mysqli_connect
- Nginx Location配置总结
- discuz mysqli_connect() 不支持 advice_mysqli_connect
- java.lang.illegalargumentexception view android.widget.listview is not a sliding drawer
- 欢送2015,喜迎2016
- 网络营销大数据实操七步走
- 深入学习js中的json
- New Year's resolution for 2016
- 2015年小结。