尺取法 poj 2566
阿新 • • 發佈:2018-08-06
ostream 序列 stdio.h names () i++ 連續 res name
尺取法:顧名思義就是像尺子一樣一段一段去取,保存每次的選取區間的左右端點。然後一直推進
解決問題的思路:
- 先移動右端點 ,右端點推進的時候一般是加
- 然後推進左端點,左端點一般是減
poj 2566
題意:從數列中找出連續序列,使得和的絕對值與目標數之差最小
思路:
- 在原來的數列開頭添加一個0
- 每次找到的區間為 [min(i,j)+1,max(i,j)]
應用尺取法的代碼:
while (r <=n) { int sub = pre[r].sum - pre[l].sum;while (abs(sub - t) < Min) { Min = abs(sub - t); ansl= min(pre[l].id, pre[r].id) + 1; ansr = max(pre[l].id, pre[r].id); ans= sub; } if (sub < t) r++; else if (sub > t) l++; else break; if (l == r) r++; }
解決問題的代碼:
#include <iostream> #include <stdio.h> #include <algorithm> #include <math.h> #include <cstring> #include <vector> #include <queue> using namespace std; const int N = 1e5 + 10; const int INF = 0x7fffffff; int n, k; int a[N]; struct node { int sum, id; }pre[N]; bool cmp(node a, node b) { return a.sum < b.sum; } int main() { while (scanf("%d%d", &n, &k) != EOF) { if (n == 0 && k == 0) break; pre[0].id = 0, pre[0].sum = 0; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); pre[i].id = i; pre[i].sum = pre[i - 1].sum + a[i]; } sort(pre, pre + n + 1, cmp); for (int i = 0; i < k; i++) { int t; scanf("%d", &t); int Min = INF; int l = 0, r = 1; int ans, ansl, ansr; while (r <=n) { int sub = pre[r].sum - pre[l].sum; while (abs(sub - t) < Min) { Min = abs(sub - t); ansl= min(pre[l].id, pre[r].id) + 1; ansr = max(pre[l].id, pre[r].id); ans = sub; } if (sub < t) r++; else if (sub > t) l++; else break; if (l == r) r++; } printf("%d %d %d\n", ans, ansl, ansr); } } return 0; }
poj 2739
題意:將一個整數分解為連續的素數之和,有多少種分法?
思路:
- 打表,先打出素數表
- 然後依次查詢是否滿足條件
用尺取法的代碼:
for (;;) { while (r < size&&sum < n) { sum += primes[r++]; } if (sum < n) break; else if (sum == n) result++; sum -= primes[l++]; }
解決問題的代碼:
#include <iostream> #include <stdio.h> #include <algorithm> #include <math.h> #include <cstring> #include <vector> #include <queue> using namespace std; #define maxn 10000+16 vector<int> primes; vector<bool> is_prime; void init_prime() { is_prime = vector<bool>(maxn + 1, true); is_prime[0] = is_prime[1] = false; for (int i = 2; i < maxn; i++) { if (is_prime[i]) { primes.push_back(i); for (int j = i * 2; j < maxn; j += i) { is_prime[j] = false; } } } } int main() { int n; init_prime(); int size = primes.size(); while (cin >> n && n) { int result = 0, sum = 0; int l = 0,r = 0; for (;;) { while (r < size&&sum < n) { sum += primes[r++]; } if (sum < n) break; else if (sum == n) result++; sum -= primes[l++]; } printf("%d\n", result); } return 0; }
poj 2100
題意:將一個整數分解為連續數平方之和,有多少種分法?
解決問題的思路:
- 右端點移動,sum+=r*r;
- 如果滿足答案就 push (l,r)
- 左端點移動 sum-=l*l;
用尺取法的代碼:
for (;;) { while (sum < n) { sq = r * r; sum += sq; r++; } if (sq > n) break; else if (sum == n) ans.push_back(make_pair(l, r)); sum -= l * l; l++; }
解決問題的代碼:
#include <iostream> #include <stdio.h> #include <algorithm> #include <math.h> #include <cstring> #include <vector> #include <queue> using namespace std; typedef long long ll; vector<pair<ll, ll>> ans; void solve(ll n) { ll l = 1, r = 1, sum = 0, sq; for (;;) { while (sum < n) { sq = r * r; sum += sq; r++; } if (sq > n) break; else if (sum == n) ans.push_back(make_pair(l, r)); sum -= l * l; l++; } ll size = ans.size(); printf("%lld\n", size); for (ll i = 0; i < size; i++) { ll ansr = ans[i].second; ll ansl = ans[i].first; printf("%lld",ansr-ansl); for (ll j=ansl;j<ansr;j++) printf(" %lld", j); printf("\n"); } } int main() { ll n; while (scanf("%lld", &n) != EOF) { if (n == 0) break; solve(n); } return 0; }
尺取法 poj 2566