實驗一 集合的表示與操做算法設計
http://www.javashuo.com/article/p-drgumzcc-gq.htmlhtml
經過此次實驗瞭解體會並掌握基本的遞歸分治算法以及貪心算法的思想,並有能力解決一些具體的問題,經過c++來實現解題的過程,進一步的熟悉算法的流程。c++
實驗大體分爲三部分: 概述 、 遞歸與分治策略 、 貪心算法 。對於每一類問題,選擇至少一道題目進行思考並用代碼驗證算法的正確性,分析算法的可行性。算法
問題描述: 一本書的頁碼從天然數1 開始順序編碼直到天然數n。書的頁碼按照一般的習慣編排,每一個頁碼都不含多餘的前導數字0。例如,第6 頁用數字6 表示,而不是06 或006 等。數字計數問題要求對給定書的總頁碼n,計算出書的所有頁碼中分別用到多少次數字0,1, 2,…,9。 編程任務: 給定表示書的總頁碼的10 進制整數n (1≤n≤109) 。編程計算書的所有頁碼中分別用到多少次數字0,1,2,…,9。 數據輸入: 輸入數據由文件名爲input.txt 的文本文件提供。每一個文件只有1 行,給出表示書的總頁碼的整數n。 結果輸出: 程序運行結束時,將計算結果輸出到文件output.txt 中。輸出文件共有10 行,在第k 行輸出頁碼中用到數字k-1 的次數,k=1,2,…,10。 輸入文件示例 input.txt 11 輸出文件示例 output.txt 1 4 1 1 1 1 1 1 1 1
使用一個10位大小的整型數組保存每個數字出現的次數,對於 \(1 \sim n\) 中出現的每個數,求出最後一位數,個數增一,最後輸出全部的 \(0 \sim 9\) 數字的個數便可。編程
#include <bits/stdc++.h> using namespace std; const int maxn = 10; int a[maxn]; void solve(int n) { while(n) { ++a[n % 10]; n /= 10; } return; } int main() { freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); int n; scanf("%d", &n); memset(a, 0, sizeof a); for(int i = 1; i <= n; ++i)solve(i); for(int i = 0; i <= 9; ++i)printf("%d\n", a[i]); }
input: 11 output: 1 4 1 1 1 1 1 1 1 1
問題描述: 正整數x 的約數是能整除x 的正整數。正整數x 的約數個數記爲div(x) 。例如,1,2, 5,10 都是正整數10 的約數,且div(10)=4 。設a 和b 是2 個正整數,a≤b,找出a 和b 之間約數個數最多的數x。 編程任務: 對於給定的2 個正整數a≤b,編程計算a 和b 之間約數個數最多的數。 數據輸入: 輸入數據由文件名爲input.txt 的文本文件提供。文件的第1 行有2 個正整數a 和b。 結果輸出: 程序運行結束時,若找到的a 和b 之間約數個數最多的數是x,將div(x)輸出到文件output.txt 中。 輸入文件示例 輸出文件示例 input.txt output.txt 1 36 9
預處理出全部 \(1 \sim maxn\) 的每個數的因數的個數,而後對每個測試遍歷 \(a \sim b\) 找出因數最多的數,輸出便可。數組
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int a[maxn]; void init() { for(int i = 2; i <= maxn; ++i) for(int j = i; j <= maxn; j += i) ++a[j]; return; } int main() { // freopen("input.in", "r", stdin); // freopen("output.out", "w", stdout); init(); int l, r; while(~scanf("%d%d", &l, &r)) { int ans = 0; for(int i = l; i <= r; ++i) ans = max(ans, a[i]); printf("%d\n", ++ans); } }
input.txt output.txt 1 36 9
問題描述: 在數據加密和數據壓縮中常須要對特殊的字符串進行編碼。給定的字母表A 由26 個小寫英文字母組成A={a,b,…,z}。該字母表產生的升序字符串是指字符串中字母按照從左到右出現的次序與字母在字母表中出現的次序相同,且每一個字符最多出現1 次。例如,a,b,ab,bc,xyz 等字符串都是升序字符串。如今對字母表A 產生的全部長度不超過6 的升序字符串按照字典序排列並編碼以下。 1 2 … 26 27 28 … a b … z ab ac … 對任意長度不超過6 的升序字符串,迅速計算出它在上述字典中的編碼。 編程任務: 對於給定的長度不超過6 的升序字符串,編程計算出它在上述字典中的編碼。 數據輸入: 輸入數據由文件名爲input.txt 的文本文件提供文件的第一行是一個正整數k,表示接下來共有k 行接下來的k 行中,每行給出一個字符串。 結果輸出: 程序運行結束時,將計算結果輸出到文件output.txt 中。文件共有k 行,每行對應於一 個字符串的編碼。 輸入文件示例 輸出文件示例 input.txt output.txt 2 1 a 2 b
手推出幾個字符串的序號後能夠看出,要求一個字符串的編號,能夠經過 \(求出k-1長的字符串的最後一個的編號+與當前要求字符串第一個字符字典序小的、長度一致的全部字符串的數量+長度減一且對應字符相同的剩餘字符串的個數的和\) 來求得待求字符串的編號,顯然三次計算都有一個公共操做: 求一個串長爲len且開頭爲a的全部字符串的數量 , 定義 \(sum(a, k)\) 表示以字符a開頭的長度爲k的字符串的數量, 顯然能夠經過全部長度爲 \(k - 1\) 開頭字符爲大於a的字符與a的拼接能夠獲得,因此咱們求出長度爲 \(k - 1\) 開頭爲 \(a + 1 \sim 26\)的全部字符串的數量和即可以獲得長度爲 \(k\) 且開頭爲a的字符串的數量,也就是說: \(sum(a, k) = \sum_{i = a + 1}^{26}sum(i, k-1)\) ,這個能夠經過遞歸的方式獲得。函數
因而整個問題的結果流程爲:測試
例如:編碼
s: cefvz加密
由於題目給出的要求字符串最大長度僅爲6,因此能夠深搜出全部的符合條件的字符串,(dfs序即爲字符串的序號),而後對於每個字符串給定一個hash值,最後對於每個詢問根據字符串的hash即可得出序號。(經過這個方法能夠生成測試數據)spa
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5; char s[10]; int sum(int i, int j) { int ret = 0; if(j == 1)return 1; else { for(int k = i + 1; k <= 26; ++k) ret += sum(k, j - 1); } return ret; } void solve() { int ans = 0; int len = strlen(s); for(int i = 1; i <= len - 1; ++i) for(int j = 1; j <= 26; ++j) ans += sum(j, i); // cout << ans << "--" << endl; for(int i = 1; i <= s[0] - 'a' + 1 - 1; ++i) ans += sum(i, len); // cout << ans << "--" << endl; for(int i = 1; i <= len - 1; ++i) { for(int j = s[i - 1] - 'a' + 1 + 1; j <= s[i] - 'a' + 1 - 1; ++j) ans += sum(j, len - i); } printf("%d\n", ++ans); } int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); int t;scanf("%d", &t); while(t--) { scanf("%s", &s); solve(); } return 0; }
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5; char s[10]; const int p = 1e3+7; unordered_map<int, int> mp; inline int gethash(int len) { int ret = 1; for(int i = 0; i <= len - 1; ++i) ret += ret * p + s[i] - 'a'; return ret; } int tot = 0; void print(int len, int n) { if(n == len) { // printf("%s", s); // for(int i = 0; i < len; ++i)printf("%c", s[i]); // printf("\n"); mp[gethash(len)] = ++tot; return; } if(n == 0) { for(int i = 0; i < 26; ++i) { s[0] = (char)(i + 'a'); print(len, n + 1); } } else { for(int i = 0; i < 26; ++i) { if(s[n - 1] < i + 'a') { s[n] = (char)(i + 'a'); print(len, n + 1); } } } return; } void init() { tot = 0; for(int i = 1; i <= 6; ++i) print(i, 0); } int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); init(); int t;scanf("%d", &t); while(t--) { scanf("%s", &s); printf("%d\n", mp[gethash(strlen(s))]); } return 0; }
input: 4 abc bciop acfuxz uvwxyz output: 352 31577 98306 313911
算法實現題 程序存儲問題 問題描述: 設有n 個程序{1,2,…, n }要存放在長度爲L 的磁帶上。程序i 存放在磁帶上的長度是li,1≤i≤n 。程序存儲問題要求肯定這n 個程序在磁帶上的一個存儲方案,使得可以在磁帶上存儲儘量多的程序。 編程任務: 對於給定的n 個程序存放在磁帶上的長度,編程計算磁帶上最多能夠存儲的程序數。 數據輸入: 由文件input.txt 給出輸入數據。第一行是2 個正整數,分別表示文件個數n 和磁帶的長度L。接下來的1 行中,有n 個正整數,表示程序存放在磁帶上的長度。 結果輸出: 將編程計算出的最多能夠存儲的程序數輸出到文件output.txt 。 輸入文件示例輸出文件示例 input.txt output.txt 6 50 5 2 3 13 8 80 20
由於要最大化存儲的數量,因此優先考慮存儲空間小的。
排序後取前面佔用空間和恰好小於等於最大空間的個數。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5; int a[maxn]; void solve() { int n, l; scanf("%d%d", &n, &l); for(int i = 1; i <= n; ++i)scanf("%d", &a[i]); sort(a + 1, a + 1 + n); int ans = 0; int sum = 0; for(int i = 1; i <= n; ++i) { sum += a[i]; if(sum <= l)++ans; else break; } printf("%d\n", ans); return; } int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); solve(); return 0; }
input: 6 50 2 3 13 8 80 20 output: 5
算法實現題 汽車加油問題 問題描述: 一輛汽車加滿油後可行駛n 千米。旅途中有若干個加油站。設計一個有效算法,指出應在哪些加油站停靠加油,使沿途加油次數最少。並證實算法能產生一個最優解。 編程任務: 對於給定的n 和k 個加油站位置,編程計算最少加油次數。 數據輸入: 由文件input.txt 給出輸入數據。第一行有2 個正整數n 和k,表示汽車加滿油後可行駛n 千米,且旅途中有k 個加油站。接下來的1 行中,有k+1 個整數,表示第k 個加油站與第k-1 個加油站之間的距離。第0 個加油站表示出發地,汽車已加滿油。第k+1 個加油站表示目的地。 結果輸出: 將編程計算出的最少加油次數輸出到文件output.txt 。若是沒法到達目的地,則輸出」No Solution」。 輸入文件示例輸出文件示例 input.txt output.txt 7 7 4 1 2 3 4 5 1 6 6
貪心考慮加油,噹噹前剩餘油量不足以到達下一站是,在這一個站加油,不然一直走下去。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5; int n, k, a[maxn]; int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); cin >> n >> k; for(int i = 0; i <= k; ++i)cin >> a[i]; int ans = 0; for(int i = 1; i <= k; ++i) { int sum = 0; int nxt = i; for(nxt = i; nxt <= k; ++nxt) { sum += a[nxt]; if(sum > n)break; } if(i == nxt) { cout << "No Solution" << endl; return 0; } i = nxt - 1; ++ans; if(nxt == k + 1)break; } cout << ans - 1 << endl; return 0; }
input: 7 7 1 2 3 4 5 1 6 6 output; 4
算法實現 最優分解問題 問題描述: 設n 是一個正整數。如今要求將n 分解爲若干個互不相同的天然數的和,且使這些天然數的乘積最大。 編程任務: 對於給定的正整數n,編程計算最優分解方案。 數據輸入: 由文件input.txt 提供輸入數據。文件的第1 行是正整數n。 結果輸出: 程序運行結束時,將計算出的最大乘積輸出到文件output.txt 中。 輸入文件示例 輸出文件示例 input.txt output.txt 10 30
若 \(n = i + j\) , 當 \(ij\) 很接近時,此時 \(i * j\) 最大,因此先將n分解爲 \(a={2 + 3 + 4 + ... + k}\) 且 \(\sum a <= n\) ,對於多出來的一部分數,倒着爲每一位加一,保證最後的乘積的最大。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int a[maxn]; int n; void solve() { for(int i = 1; i * i / 4 <= n; ++i) a[i] = i; int sum = 0; int last; for(int i = 2; i * i / 4 <= n; ++i) { sum += a[i]; if(sum >= n) { sum -= a[i]; sum = n - sum; last = i - 1; break; } } while(sum) { ++a[last]; --sum; if(!sum)break; for(int i = last; i >= 2; --i) { --sum; ++a[i]; if(!sum)break; } } int ans = 1; for(int i = 2; i <= last; ++i)ans *= a[i]; cout << ans << endl; return; } int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); while(~scanf("%d", &n)) solve(); return 0; }
intut: 10 output: 30
算法實現題 衆數問題 問題描述: 給定含有n 個元素的多重集合S,每一個元素在S 中出現的次數稱爲該元素的重數。多重集S 中重數最大的元素稱爲衆數。 例如,S={1,2,2,2,3,5}。 多重集S 的衆數是2,其重數爲3。 編程任務: 對於給定的由n 個天然數組成的多重集S,編程計算S 的衆數及其重數。 數據輸入: 輸入數據由文件名爲input.txt 的文本文件提供。文件的第1 行多重集S 中元素個數n;接下來的n 行中,每行有一個天然數。 結果輸出: 程序運行結束時,將計算結果輸出到文件output.txt 中。輸出文件有2 行,第1 行給出衆數,第2 行是重數。 輸入文件示例 輸出文件示例 input.txt output.txt 6 2 1 3 2 2 2 3 5
排序後二分查找到每個數的出現次數,記錄下來取最大值便可。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5; int a[maxn]; int ans, ansn; int n; void solve() { scanf("%d", &n); for(int i = 1; i <= n; ++i)scanf("%d", &a[i]); int l, r; l = r = 1; int t; sort(a + 1, a + 1 + n); ans = ansn = 0; for(int i = 1; i <= n; ++i) { l = lower_bound(a + 1, a + 1 + n, a[i]) - a - 1; r = upper_bound(a + 1, a + 1 + n, a[i]) - a - 1; t = r - l; if(t >= ansn) { ans = a[i]; ansn = t; i = r; } } printf("%d\n%d\n", ans, ansn); return; } int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); solve(); return 0; }
input: 6 1 2 2 2 3 5 output: 2 3
算法實現題 整數因子分解問題 問題描述: 大於1 的正整數n 能夠分解爲:n=x1*x2*…*xm。 例如,當n=12 時,共有8 種不一樣的分解式: 12=12; 12=6*2; 12=4*3; 12=3*4; 12=3*2*2; 12=2*6; 12=2*3*2; 12=2*2*3 。 編程任務: 對於給定的正整數n,編程計算n 共有多少種不一樣的分解式。 數據輸入: 由文件input.txt 給出輸入數據。第一行有1 個正整數n (1≤n≤2000000000)。 結果輸出: 將計算出的不一樣的分解式數輸出到文件output.txt 。 輸入文件示例 輸出文件示例 input.txt output.txt 12 8
暴力遞歸尋找出每個可能分解出的因式,計數便可。(對於大於1e7的某些整數這樣的作法可能超過1s)
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll ans, n; void solve(ll x) { if(x == 1) ++ans; else for(int i = 2; i <= x; ++i) if(!(x % i)) solve(x / i); } int main() { freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); while(cin >> n) { ans = 0; solve(n); cout << ans << endl; } return 0; }
intput: 12 100 233 123456 10000000 output: 8 26 1 2496 3112896