計算機軟件能力認證考試系統
問題描述
試題編號: | 202309-3 |
試題名稱: | 梯度求解 |
時間限制: | 1.0s |
內存限制: | 512.0MB |
問題描述: | 背景 梯度下降算法中,求解函數在一點處對某一自變量的偏導數是十分重要的。小 C 負責實現這個功能,但是具體的技術實現,他還是一頭霧水,希望你來幫助他完成這個任務。 問題描述 求算多元函數在一點處對某一自變量的偏導數的方法是:將函數的該自變量視為單一自變量,其余自變量認為是常數,運用一元函數求導的方法求出該偏導數表達式,再代入被求算的點的坐標即可。 例如,要求算 u=x1?x1?x2 對 x1 在 (1,2) 處的偏導數,可以將 x2 視為常數,依次應用求導公式。先應用乘法的求導公式:(x1?(x1?x2))′=x1′(x1?x2)+x1(x1?x2)′;再應用常數與變量相乘的求導公式,得到 x1′?x1?x2+x1?x2?x1′;最后應用公式 x′=1 得到 1?x1?x2+x1?x2?1。整理得 ?u?x1=2x2?x1。再代入 (1,2) 得到 ?u?x1(1,2)=4。 常見的求導公式有: (是常數)c′=0 (c是常數) x1; 輸入的第一行是由空格分隔的兩個正整數 n、m,分別表示要求解函數中所含自變量的個數和要求解的偏導數的個數。 輸入的第二行是一個逆波蘭式,表示要求解的函數 f。其中,每個元素用一個空格分隔,每個元素可能是: 一個自變量 xi,用字符 x 后接一個正整數表示,表示第 i 個自變量,其中 i=1,2,…,n。例如,x1 表示第一個自變量 x1。 輸出格式 輸出 m 行,每行一個整數,表示對應的偏導數對 109+7 取模的結果。即若結果為 y,輸出為 k,則保證存在整數 t,滿足 y=k+t?(109+7) 且 0≤k<109+7。 樣例 1 輸入 樣例 1 輸出 樣例 1 說明 對 x1 求偏導得 ?u?x1=3x12+x2。代入 (2,3) 得到 ?u?x1(2,3)=15。 對 x2 求偏導得 ?u?x2=x1。代入 (3,4) 得到 ?u?x2(3,4)=3。 樣例 2 輸入 樣例 2 輸出 樣例 2 說明 因為 u 中實際上不含 x1 和 x3,對這兩者求偏導結果均為 0。 對 x2 求偏導得 ?u?x2=3x22?1010。 評測用例規模與約定 當計算整數 n 對 M 的模時,若 n 為負數,需要注意將結果調整至區間 [0,M) 內。 |
解析:
參考來源:第31次CCF計算機軟件能力認證 - ~Lanly~ - 博客園 (cnblogs.com)
從題目中可以看逆波蘭式就是表達樹的后序遍歷,所以我們可以利用二叉樹的遞歸遍歷進行求解。
同時,我們可以利用vector向量建立二叉樹,用鏈表也行,但是會比較麻煩。
具體請看代碼:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
using namespace std;
typedef long long LL;
#define CH 1
#define CONST 2
#define OP 3
const int mod = 1e9 + 7;
int n, m;
vector<int>lc;
vector<int>rc;
vector<int>info;
vector<int>kind;
stack<int>stk;
vector<int>a(200);
// 定義遞歸函數 solve,計算表達式樹節點的值和導數
pair<int, int> solve(int u, int x) {if (kind[u] == CH) {return pair<int, int>{a[info[u]], (info[u] == x)};}else if (kind[u] == CONST) {return pair<int, int>{info[u], 0};}else {auto lans = solve(lc[u], x), rans = solve(rc[u], x);int sum = 0, dsum = 0;if (info[u] == '+') {sum = lans.first + rans.first;dsum = lans.second + rans.second;}else if (info[u] == '-') {sum = lans.first - rans.first;dsum = lans.second - rans.second;}else {sum = 1ll*lans.first * rans.first % mod;dsum = (1ll*lans.first * rans.second%mod + 1ll*lans.second * rans.first%mod);//1ll 是一種常見的編碼技巧,用于將數字字面量轉換為長長整型(long long)類型。}sum = (sum % mod + mod) % mod;dsum = (dsum % mod + mod) % mod;return pair<int, int>{sum, dsum};}
}
int main() {cin >> n >> m;string s;getline(cin, s);getline(cin, s);int cnt = 0;istringstream q(s);//q 是一個 istringstream 對象,用于從字符串中讀取數據。//getline(qwq, s, ' ') 是 C++ 中的輸入流操作,//用于從輸入流 qwq 中讀取一行(以換行符 '\n' 結束的一行)并存儲到字符串 s 中,//直到遇到指定的分隔符(在這里是空格 ' ')為止。while (getline(q, s, ' ')) {if (s.size() == 1 && (s[0] == '+' || s[0] == '*' || s[0] == '-')) {int rson = stk.top();stk.pop();int lson = stk.top();stk.pop();lc.push_back(lson);rc.push_back(rson);info.push_back(s[0]);kind.push_back(OP);stk.push(cnt);cnt++;}else if (s[0] == 'x') {int x = stoi(s.substr(1));//subStr 是從第二個字符開始的子串,stoi 將其轉換為整數。x--;lc.push_back(-1);rc.push_back(-1);info.push_back(x);kind.push_back(CH);stk.push(cnt);cnt++;}else {int x = stoi(s);lc.push_back(-1);rc.push_back(-1);info.push_back(x);kind.push_back(CONST);stk.push(cnt);cnt++;}}int root = stk.top();// 處理每個查詢for (int i = 1; i <= m; i++) {int x;cin >> x;x--;//由于我們是從0開始記錄,所以這里要-1for (int j = 0; j < n; j++) {cin >> a[j];}cout << solve(root, x).second << endl;}return 0;
}