網易2017 實習生筆試題


1.【編程題】消除重復元素

  時間限制:1秒

  空間限制:32768K

  小易有一個長度為n序列,小易想移除掉里面的重復元素,但是小易想是對於每種元素保留最后出現的那個。小易遇到了困難,希望你來幫助他。 

  輸入描述

  輸入包括兩行:
  第一行為序列長度n(1 ≤ n ≤ 50)
  第二行為n個數sequence[i](1 ≤ sequence[i] ≤ 1000),以空格分隔

  輸出描述

  輸出消除重復元素之后的序列,以空格分隔,行末無空格

  輸入例子

  9
  100 100 100 99 99 99 100 100 100

  輸出例子

  99 100

題目分析

1.解法一:這道題目里面序列長度並不長,最多不會超過50個,因此使用搜索的方式也可以,雖然效率不高,但是可以使用。參考其他人的代碼,對於去重有一種技巧,這道題目是從后向前搜索,因此在搜索時候,將重復的數字置位,這里因為數的范圍在1-1000,因此置位為0.(注意審題。)代碼如下:

#include<iostream>
using namespace std; int main() { int n; cin >> n; int c[50]; for (int i = 0; i < n; i++) cin >> c[i]; //倒序
    for (int i = n - 1; i >= 0; i--) { int b = c[i]; for (int j = i - 1; j >= 0; j--) { if (c[j] - b == 0) c[j] = 0; } } //輸出
    for (int i = 0; i < n; i++) { if (c[i] != 0) { cout << c[i]; if (i < n - 1) cout << ' '; } } return 0; }

 2.解法2:這里涉及到一個數據存儲的問題,提煉出本質:就是說從后向前搜索,將數據存入數據結構中,首先判重復,檢查已經存下的數據中是否有該數據,如果重復就不加入數據結構中。

  有人使用隊列結構:使用一個隊列,對於每次輸入,判斷該數是否已經存在,若存在,刪除。然后壓入隊列。

  但更多得是使用set:set是一種關容器,是關鍵字的簡單集合,當只想知道一個值是否存在的時候,set是最有用的。

#include <iostream> #include <vector> #include <set>
using namespace std; int main() { vector<int> input, res; set<int> s; int n,x; //輸入
    cin >> n; for (int i = 0; i < n; i++) { cin >> x; input.push_back(x); } //set 查重 從后向前查重
    for (int i = input.size() - 1; i >= 0; i--) { if (s.find(input[i]) == s.end())  //s.end()
 { s.insert(input[i]); res.push_back(input[i]);//注意set會對元素順序根據關鍵字改變 因此需要存放到vector中 } }   //倒序輸出 cout << res[res.size() - 1]; for (int i = res.size() - 2; i >= 0; i--) cout << " " << res[i]; return 0; }

 2.【編程題】雙核處理

  時間限制:1秒

  空間限制:32768K

  一種雙核CPU的兩個核能夠同時的處理任務,現在有n個已知數據量的任務需要交給CPU處理,假設已知CPU的每個核1秒可以處理1kb,每個核同時只能處理一項任務。n個任務可以按照任意順序放入CPU進行處理,現在需要設計一個方案讓CPU處理完這批任務所需的時間最少,求這個最小的時間。
  輸入描述:
  輸入包括兩行:
  第一行為整數n(1 ≤ n ≤ 50)
  第二行為n個整數length[i](1024 ≤ length[i] ≤ 4194304),表示每個任務的長度為length[i]kb,每個數均為1024的倍數。

  輸出描述:

  輸出一個整數,表示最少需要處理的時間

   輸入例子:

  5
  3072 3072 7168 3072 1024

   輸出例子:9216

題目分析和解答

  1.解法1:這道題初拿到感覺到有點難下手,后來正常想,假如說所有任務需要的處理的時長的Task-sum,雙核的處理器的情況下,每一個處理器處理Task-sum/2的情況下就是最優的狀態,但是事實上單個任務的Task不可分割,所以應該盡量雙核工作的時長應該盡量逼近Task_sum/2。這里實際上就轉換為0-1背包問題。

  這里的解法就是非常中規中矩的動態規划,在空間上有較大的可優化空間,具體見解法2.在做解題的時候有幾個注意點:

  (1)數組的長度不定,背包(內核的處理時長Task_sum/2)容量也需要根據測試用例來確定,因此需要動態申請二維數組,也需要在最后動態釋放空間。申請的二維數組的長度也需要注意一下,邊界條件和填表時遞增的初始值也要注意。

  (2)另外一個容易出錯點:我在做題的時候,輸入進來的數據用的是vector,下標是(0-n-1),但是在用的時候,下標用的是(1-n),因此一開始的時候通過率為90%,后來發現是我的下標弄錯的原因,所以做題目一定要仔細。

  (3)最后一個注意時:就是最后的結果,實際上獲得的結果res是對於一個內核來說,最逼近(task_sum/2)的狀態,它不可能超過task_sum/2,因此所取的值一定是task_sum - res。

#include <iostream> #include <algorithm> #include <vector>
using namespace std; int main() { int n = 0; //任務總數;
    int t; int sum=0,half =0; vector<int> task; //輸入
    cin >> n; task.push_back(0);  // task 的第一位
    for (int i = 0; i < n; i++) { cin >> t; t = t/1024; sum += t; task.push_back(t); } half = sum / 2; // 申請二維數組
    int **c = new int *[n+1];  //+1
    for (int i = 0; i <=n; i++) c[i] = new int [half+1]; for (int i = 0; i <= n; i++) c[i][0] = 0; for (int j = 0; j <= half; j++) c[0][j] = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= half; j++) { if (task[i] > j) c[i][j] = c[i - 1][j]; else c[i][j] = max(c[i - 1][j - task[i]] + task[i], c[i - 1][j]); } } cout<<(sum-c[n][half])*1024; //刪除申請的二維數組
    for(int i =0;i<=n;i++) delete []c[i]; delete []c; return 0; }

 

  2.解法2:優化的動態規划,在空間上進行優化;

#include <iostream> #include <algorithm> #include <vector>
using namespace std; int main() { int n = 0; //任務總數;
    int t; int sum =0,half =0; vector<int> task; //輸入
    cin >> n; for (int i = 0; i < n; i++) { cin >> t; t = t/1024; sum += t; task.push_back(t); } half = sum / 2; int *dp = new int[half+1];  //+1
    for(int i =0;i<=half;i++ ) dp[i]=0; for(int i =0;i<n;i++)  //注意 for里面
 { for(int j = half;j>=task[i];j--) { dp[j] = max(dp[j],dp[j-task[i]]+task[i]); } } cout<<(sum-dp[half])*1024; delete []dp; return 0; }

3.[編程題] 趕去公司

  時間限制:1秒

  空間限制:32768K

  終於到周末啦!小易走在市區的街道上准備找朋友聚會,突然服務器發來警報,小易需要立即回公司修復這個緊急bug。假設市區是一個無限大的區域,每條街道假設坐標是(X,Y),小易當前在(0,0)街道,辦公室在(gx,gy)街道上。小易周圍有多個出租車打車點,小易趕去辦公室有兩種選擇,一種就是走路去公司,另外一種就是走到一個出租車打車點,然后從打車點的位置坐出租車去公司。每次移動到相鄰的街道(橫向或者縱向)走路將會花費walkTime時間,打車將花費taxiTime時間。小易需要盡快趕到公司去,現在小易想知道他最快需要花費多少時間去公司。 
  輸入描述:
  輸入數據包括五行:
  第一行為周圍出租車打車點的個數n(1 ≤ n ≤ 50)
  第二行為每個出租車打車點的橫坐標tX[i] (-10000 ≤ tX[i] ≤ 10000)
  第三行為每個出租車打車點的縱坐標tY[i] (-10000 ≤ tY[i] ≤ 10000)
  第四行為辦公室坐標gx,gy(-10000 ≤ gx,gy ≤ 10000),以空格分隔
  第五行為走路時間walkTime(1 ≤ walkTime ≤ 1000)和taxiTime(1 ≤ taxiTime ≤ 1000),以空格分隔
  輸出描述:
  輸出一個整數表示,小易最快能趕到辦公室的時間
  輸入例子:
  2
  -2 -2
  0 -2
  -4 -2
  15 3
  輸出例子:
  42

 題目分析和解答:

  這一道題目比較簡單,共有兩種去到公司的方法,走路或者打車,題意提供的打車點不超過50個,也就是說在最多選擇的情況下不會超過51種方法,因此完全可以采用暴力枚舉的方法解決這個問題。程序如下。

#include<iostream> #include<cmath> #include<vector>
using namespace std; int main() { int n,gx,gy,wtime,ttime; vector<int> tx, ty; int min = 0, res = 0; cin >> n; for (int i = 0; i < n; i++) { int temp; cin >> temp; tx.push_back(temp); } for (int i = 0; i < n; i++) { int temp; cin >> temp; ty.push_back(temp); } cin >> gx >> gy >> wtime >> ttime; // walking value;
    min = (abs(gx) + abs(gy)) * wtime; for (int i = 0; i < n; i++) { res = (abs(tx[i]) + abs(ty[i])) * wtime + (abs(gx - tx[i]) + abs(gy - ty[i])) *ttime; if (res < min) min = res; } cout << min; return 0; }

4.【編程題】 調整隊形

  時間限制:1秒

  空間限制:32768K

  在幼兒園有n個小朋友排列為一個隊伍,從左到右一個挨着一個編號為(0~n-1)。其中有一些是男生,有一些是女生,男生用'B'表示,女生用'G'表示。小朋友們都很頑皮,當一個男生挨着的是女生的時候就會發生矛盾。作為幼兒園的老師,你需要讓男生挨着女生或者女生挨着男生的情況最少。你只能在原隊形上進行調整,每次調整只能讓相鄰的兩個小朋友交換位置,現在需要盡快完成隊伍調整,你需要計算出最少需要調整多少次可以讓上述情況最少。例如:
  GGBBG -> GGBGB -> GGGBB
  這樣就使之前的兩處男女相鄰變為一處相鄰,需要調整隊形2次 
  輸入描述:
  輸入數據包括一個長度為n且只包含G和B的字符串.n不超過50.
  輸出描述:
  輸出一個整數,表示最少需要的調整隊伍的次數
  
  輸入例子:
  GGBBG

  輸出例子:
  2

題目分析和解答:

  這道題目並不復雜,但是一開始想復雜了,看到最小調整隊伍的次數的時候,一開始就想到的是動態規划里“字符串相似度”問題解決,但是考慮了之后發現比較難發現最優子結構,然后重新審題,字符串長度最多不超過50,可以知道這不是一個復雜的問題,問題的復雜度也不會高到哪里去。

  因此實際上交換的結果就是女生和女生在一起,男生和男生在一起,只有兩種可能:GG……GBB……B,或者BB……BGG……B,實際上最終的交換次數可以如下表計算:

Index

0

1

2

3

4

5

6

 

G

G

B

B

G

G

B

Swap

B

B

B

G

G

G

G

  實際上Boy的交換次數計算為: (2-0)+(3-1)+(6-2) = 8次;

  因此按照思路,計算兩種安排可能下的交換次數,取最小值即為答案。

#include <iostream> #include <string>
using namespace std; int main() { string list; int bnum = 0, gnum = 0; int b_index_sum = 0, g_index_sum = 0; int b_move = 0, g_move = 0; cin >> list; for (int i = 0; i < list.length(); i++) { if (list[i] == 'B')//boy if == 一開始寫錯= 啊低級錯誤
 { b_index_sum += i; bnum++; } if (list[i] == 'G')//girl
 { g_index_sum += i; gnum++; } } //boy-move-step
    b_move = b_index_sum - (bnum - 1)*bnum / 2; //girl-move-step
    g_move = g_index_sum - (gnum - 1)*gnum / 2; cout << ((b_move < g_move) ? b_move : g_move);  //運算符的優先級 注意一下<< 優先級大於三目運算
    return 0; }

 5.【編程題】魔力手環

  時間限制:1秒

  空間限制:32768K

  小易擁有一個擁有魔力的手環上面有n個數字(構成一個環),當這個魔力手環每次使用魔力的時候就會發生一種奇特的變化:每個數字會變成自己跟后面一個數字的和(最后一個數字的后面一個數字是第一個),一旦某個位置的數字大於等於100就馬上對100取模(比如某個位置變為103,就會自動變為3).現在給出這個魔力手環的構成,請你計算出使用k次魔力之后魔力手環的狀態。 
  輸入描述:
  輸入數據包括兩行:
  第一行為兩個整數n(2 ≤ n ≤ 50)和k(1 ≤ k ≤ 2000000000),以空格分隔
  第二行為魔力手環初始的n個數,以空格分隔。范圍都在0至99.
  輸出描述:
  輸出魔力手環使用k次之后的狀態,以空格分隔,行末無空格。

輸入例子:
  3 2
  1 2 3
  
 輸出例子:
  8 9 7

 題目分析和解答:

   這一道題目如果直接來求解,代碼很簡單,但是問題就出在 k(1 ≤ k ≤ 2000000000) 上,k的值太大,則很容易就超時超空間,參看了別人的解答,發現這道題目是一道蠻典型的矩陣快速冪模板題,矩陣快速冪用於求解這種大量遞推次數的題目,求解的關鍵也是獲得遞推矩陣。

  在這道題目中,假設初始狀態 T= (x, x, x3…… xn,遞推矩陣為A,則第K次后狀態為T= AK * T0

  根據題意,遞推矩陣為如下類似所示,使用快速冪取模模板求解即可,注意矩陣中0比較多,這一點可以優化一下矩陣乘法

      

#include <iostream> #include <cstdio> #include <cstring>
using namespace std; const int NUM = 51; const int MOD = 100; struct mat{ int n, m;  //行,列
    int data[NUM][NUM]; }; /* mat mul(mat A, mat B) //矩陣乘法 這題里需要考慮一下 { mat ret; ret.n = A.n; //行 ret.m = B.m; //列 for (int i = 0; i < A.n; i++) { for (int j = 0; j < B.m; j++) { ret.data[i][j] = 0; for (int k = 0; k < B.n; k++) //A.m = B.n ret.data[i][j] += (A.data[i][k] * B.data[k][j]); if (ret.data[i][j] >= MOD) //減少一定的取模運算 取模操作耗時 ret.data[i][j] %= MOD; } } return ret; }*/

// 優化代碼
mat mul(mat A, mat B)  //矩陣乘法 這題里需要考慮一下
{ mat ret; ret.n = A.n;  //
    ret.m = B.m;  //
 memset(ret.data,0,sizeof(ret.data)); for (int i = 0; i < A.n; i++) { for (int k = 0; k < B.n; k++) { if(A.data[i][k]){ for (int j = 0; j < B.m; j++) { //A.m = B.n
                    ret.data[i][j] += (A.data[i][k] * B.data[k][j]); if (ret.data[i][j] >= MOD) ret.data[i][j] %= MOD; } } } } return ret; } mat mypow(mat A, long long n) { mat ans; ans.n = ans.m = A.n; if (n == 1) return A; //ans 初始化為單位矩陣
    memset(ans.data, 0, sizeof(ans.data)); for (int i = 0; i < ans.n; i++) ans.data[i][i] = 1; if (n == 0) return ans; while (n) { if (n & 1) ans = mul(ans, A); A = mul(A, A); n >>= 1; } return ans; } int main() { int n; long long k; //k 次操作
    mat base,org,ret; memset(org.data, 0, sizeof(org.data)); cin >> n >> k;  //n個數 k次變化
    for (int i = 0; i < n; i++) { int d; cin >> d; org.data[i][0] = d; } //org為源狀態向量
    org.n = n; org.m = 1; //構建遞推矩陣;
    base.n = base.m = n; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { if (i == j || (i + 1) % n == j) base.data[i][j] = 1; else
                base.data[i][j] = 0; } base = mypow(base, k); ret = mul(base, org); for (int i = 0; i < n-1; i++) { cout << ret.data[i][0] << " "; } cout << ret.data[n-1][0]; return 0; }
6.【編程題】 工作安排

  時間限制:1秒

  空間限制:32768K

  現在有n位工程師和6項工作(編號為0至5),現在給出每個人能夠勝任的工作序號表(用一個字符串表示,比如:045,表示某位工程師能夠勝任0號,4號,5號工作)。現在需要進行工作安排,每位工程師只能被安排到自己能夠勝任的工作當中去,兩位工程師不能安排到同一項工作當中去。如果兩種工作安排中有一個人被安排在的工作序號不一樣就被視為不同的工作安排,現在需要計算出有多少種不同工作安排計划。 
  輸入描述:
  輸入數據有n+1行:
  第一行為工程師人數n(1 ≤ n ≤ 6)
  接下來的n行,每行一個字符串表示第i(1 ≤ i ≤ n)個人能夠勝任的工作(字符串不一定等長的)
  
 輸出描述:
  輸出一個整數,表示有多少種不同的工作安排方案
·
  輸入例子:
  6
  012345
  012345
  012345
  012345
  012345
  012345

  輸出例子:

  720
題目分析解析:

   這一道題目看起來就比較適合回溯法。解題代碼如下所示:

#include<iostream> #include<string> #include<vector> #include<set>
using namespace std; vector<string> v; set<int> ret; int n; int tot =0; void track_back(int cur) { if (cur == n) tot++;  //為一種安排
    else { string curr = v[cur]; for (int i = 0; i < curr.length(); i++) { int s = curr[i] - '0'; if (ret.find(s) == ret.end()) { ret.insert(s); track_back(cur + 1); ret.erase(s); } } } } int main() { cin >> n; for (int i = 0; i < n; i++) { string temp; cin >> temp; v.push_back(temp); } track_back(0); cout << tot; return 0; }

7.【編程題】 集合

  時間限制:1秒

  空間限制:32768K

  小易最近在數學課上學習到了集合的概念,集合有三個特征:1.確定性 2.互異性 3.無序性.
  小易的老師給了小易這樣一個集合:
  S = { p/q | w ≤ p ≤ x, y ≤ q ≤ z }
  需要根據給定的w,x,y,z,求出集合中一共有多少個元素。小易才學習了集合還解決不了這個復雜的問題,需要你來幫助他。 
  輸入描述:
  輸入包括一行:
  一共4個整數分別是w(1 ≤ w ≤ x),x(1 ≤ x ≤ 100),y(1 ≤ y ≤ z),z(1 ≤ z ≤ 100).以空格分隔
  
  輸出描述:
  輸出集合中元素的個數
  輸入例子:
  1 10 1 1

  輸出例子:
  10

 題目解析:

  這一道題目很簡單,就是一個set判重的問題,但是注意一下分數與浮點數的問題。

  看別人的建議說不能直接相除,因為浮點數不精確。但我用了double類型的代碼也AC了。所以覺得有點奇怪。還是要嘗試搞明白一點。

  建議說建一個結構體來保存分數,主要思想如下所示。

 

 int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } int g = gcd(i, j); s.insert(make_pair(i/g, j/g));  

 

 

 

#include<iostream> #include<set>
using namespace std; int main() { set<double> ret; int w, x, y, z; cin >> w >> x >> y >> z;  //輸入

    for (int i = w; i <= x; i++) for (int j = y; j <= z; j++) { double temp = (double)i / (double)j; if (ret.find(temp) == ret.end()) ret.insert(temp); } cout<<ret.size(); return 0; }

8.[編程題] 奇怪的表達式求值

  時間限制:1秒

  空間限制:32768K

  常規的表達式求值,我們都會根據計算的優先級來計算。比如*/的優先級就高於+-。但是小易所生活的世界的表達式規則很簡單,從左往右依次計算即可,而且小易所在的世界沒有除法,意味着表達式中沒有/,只有(+, - 和 *)。現在給出一個表達式,需要你幫忙計算出小易所在的世界這個表達式的值為多少 
  輸入描述:
  輸入為一行字符串,即一個表達式。其中運算符只有-,+,*。參與計算的數字只有0~9.
  保證表達式都是合法的,排列規則如樣例所示。
  輸出描述:
  輸出一個數,即表達式的值
  輸入例子:
  3+5*7
  輸出例子:
  56

 題目解析:

  這道題目很簡單,邏輯理清楚即可,注意一下ASCII碼與整型之間的轉換。

#include<iostream> #include<string>
using namespace std; int main() { string s; int res; cin >> s; res = s[0]; int i = 0; while (i <= s.length() - 1){ if (s[i + 1] == '+') res = res + s[i + 2]; if (s[i + 1] == '-') res = res - s[i + 2]; if (s[i + 1] == '*') res = res * s[i + 2]; i += 2; } cout << res; return 0; }
9.[編程題] 塗棋盤

  時間限制:1秒

  空間限制:32768K

  小易有一塊n*n的棋盤,棋盤的每一個格子都為黑色或者白色,小易現在要用他喜歡的紅色去塗畫棋盤。小易會找出棋盤中某一列中擁有相同顏色的最大的區域去塗畫,幫助小易算算他會塗畫多少個棋格。 
  輸入描述:
  輸入數據包括n+1行:
  第一行為一個整數n(1 ≤ n ≤ 50),即棋盤的大小
  接下來的n行每行一個字符串表示第i行棋盤的顏色,'W'表示白色,'B'表示黑色
  輸出描述:
  輸出小易會塗畫的區域大小
  輸入例子:
  3
  BWW
  BBB
  BWB
  輸出例子:
  3

 題目解析:

   這道題目題意有一點不清,但是本質上不難的題目,就是注意一下是連續的相同顏色的區域。

#include<iostream> #include<string> #include<vector> #include<algorithm>
using namespace std; int main() { int n; vector<string> s; cin >> n; for (int i = 0; i < n; i++) { string temp; cin >> temp; s.push_back(temp); } int max = 0; for (int j = 0; j < n; j++) { int count = 1; for (int i = 0; i < n-1; i++) { if (s[i][j] == s[i+1][j]) { count++; max = ((max > count) ? max : count); } else count = 1; } } cout << max; return 0; }

 10.[編程題] 小易記單詞

  時間限制:1秒

  空間限制:32768K

  小易參與了一個記單詞的小游戲。游戲開始系統提供了m個不同的單詞,小易記憶一段時間之后需要在紙上寫出他記住的單詞。小易一共寫出了n個他能記住的單詞,如果小易寫出的單詞是在系統提供的,將獲得這個單詞長度的平方的分數。注意小易寫出的單詞可能重復,但是對於每個正確的單詞只能計分一次。 
  輸入描述:
  輸入數據包括三行:
     第一行為兩個整數n(1 ≤ n ≤ 50)和m(1 ≤ m ≤ 50)。以空格分隔
  第二行為n個字符串,表示小易能記住的單詞,以空格分隔,每個單詞的長度小於等於50。
  第三行為m個字符串,系統提供的單詞,以空格分隔,每個單詞的長度小於等於50。
  輸出描述:
  輸出一個整數表示小易能獲得的分數
  輸入例子:
  3 4
  apple orange strawberry
  strawberry orange grapefruit watermelon
  輸出例子:
  136

 題目解析:

  這題也很簡單,就是一個去重的問題。

#include<iostream> #include<set> #include<vector> #include<string>
using namespace std; int main() { int n, m; set<string> sys; set<string> mem; int ret = 0; cin >> n >> m; for (int i = 0; i < n; i++) { string temp; cin >> temp; mem.insert(temp); } for (int i = 0; i < m; i++) { string temp; cin >> temp; sys.insert(temp); } for(set<string>::iterator it=mem.begin();it!=mem.end();it++) { if(sys.find(*it)!=sys.end()) ret += (it->length()) * (it->length()); } cout << ret; return 0; }
11 [編程題] 堆磚塊

  時間限制:1秒

  空間限制:32768K

  小易有n塊磚塊,每一塊磚塊有一個高度。小易希望利用這些磚塊堆砌兩座相同高度的塔。為了讓問題簡單,磚塊堆砌就是簡單的高度相加,某一塊磚只能使用在一座塔中一次。小易現在讓能夠堆砌出來的兩座塔的高度盡量高,小易能否完成呢。 
  輸入描述:
  輸入包括兩行:
  第一行為整數n(1 ≤ n ≤ 50),即一共有n塊磚塊
  第二行為n個整數,表示每一塊磚塊的高度height[i] (1 ≤ height[i] ≤ 500000)
  輸出描述:
  如果小易能堆砌出兩座高度相同的塔,輸出最高能拼湊的高度,如果不能則輸出-1.
  保證答案不大於500000。

  輸入例子:
  3
  2 3 5
  輸出例子:
  5
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxh = 500000 + 5;
int a[55];
int dp[2][maxh];

int main()
{
    int n;
    int sum = 0;
    cin >> n;
    for (int i = 1; i <=n; i++)
    {
        cin >> a[i];
        sum += a[i];  //由題意得答案最大不超過500000???
    }

    memset(dp[0], -1, sizeof(dp[0]));  // why -1
    dp[0][0] = 0;
    int t = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= sum; j++)
        {
            dp[t][j] = dp[t ^ 1][j];  //【1】
            if ((j + a[i] <= sum) && (dp[t ^ 1][j + a[i]] >= 0))
                dp[t][j] = max(dp[t][j], dp[t ^ 1][j + a[i]] + a[i]);
            if ((a[i] - j >= 0) && (dp[t ^ 1][a[i] - j] >= 0))
                dp[t][j] = max(dp[t][j], dp[t ^ 1][a[i] - j] + a[i] - j);
            if (j - a[i] >= 0 && dp[t ^ 1][j - a[i]] >= 0)
                dp[t][j] = max(dp[t][j], dp[t ^ 1][j - a[i]]);
        }
        t ^= 1;
    }
    cout << (dp[t ^ 1][0] == 0 ? -1 : dp[t ^ 1][0]);

    return 0;
}

 

12.[編程題] 分餅干

  時間限制:1秒

  空間限制:32768K

  易老師購買了一盒餅干,盒子中一共有k塊餅干,但是數字k有些數位變得模糊了,看不清楚數字具體是多少了。易老師需要你幫忙把這k塊餅干平分給n個小朋友,易老師保證這盒餅干能平分給n個小朋友。現在你需要計算出k有多少種可能的數值 
  輸入描述:
  輸入包括兩行:
  第一行為盒子上的數值k,模糊的數位用X表示,長度小於18(可能有多個模糊的數位)
  第二行為小朋友的人數n
  輸出描述:
  輸出k可能的數值種數,保證至少為1
  輸入例子:
  9999999999999X
  3
  輸出例子:
  4

 題目解析:

  這一道題剛拿到的時候第一個反應是使用回溯法來做,恩,然后發現思路不對。而且數據太龐大了,接近18位的數據位,產生的解空間一定大得驚人,然后真的沒想到可以用動態規划來做。
  忍不住感嘆動態規划真的很神奇,而且最最關鍵的一步就是找到狀態轉移方程,將問題抽象出來,真的是很奇妙啊。也因此感嘆自己的動態規划學習得有些浮於表面,也缺少練習,在動態規划的優化上也有所欠缺。

  這道題目里含有求模的知識點在里面,所以解題的基本思路是基於兩點:

  a. (a + b) mod n = (a mod n+ b mod n) mod n;  因此舉個簡單的例子,47%3 =(( 4+4+4……+4)+7)%3 =  ((4%3)*10 + 7%3)%3  =((4%3)*10 + 7)%3 

  b.動規數組dp[i][j]:i 是指第i個數,j是指余數,dp[i][j]表示第i個數產生余數j的可能值的個數,聽起來有點繞,但實際上很巧妙。

  注意這里的數組可以簡化到二維數組甚至是一維數組

[#include<iostream> #include<string> #include<cstring>
using namespace std; //[1] long long 類型 輸出的結果可能會超過 int型 考慮到數位超過18 如果分的人數少,而且X的個數較多 // 如:9XXXXXXXXXXXXXXXXX 1 其結果很容易就超過int的容納量 //[2] 此處為基礎動態規划 數組第二維度 的大小實際上應該根據人數n來決定,但是題目沒有給出來,所以設定較大一個數
long long dp[20][10000];   //18會出錯 19/20
int main() { string str; int n; cin >> str >> n; memset(dp, 0, sizeof(dp)); dp[0][0] = 1;//?

    for (int i = 1; i <= str.length(); i++)  //從前向后遍歷 (此處應為1 -length();否則i-1報錯,數組溢出)
 { for (int j = 0; j < n; j++)  //余數從0 - n;
 { if (str[i-1] == 'X') for (int k = 0; k <= 9; k++) dp[i][(j * 10 + k) % n] += dp[i - 1][j]; else dp[i][(j * 10 + str[i-1] - '0') % n] += dp[i - 1][j]; } } cout << dp[str.length()][0]; return 0; }

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM