CSP-31补题日记--梯度求解

202309-3-梯度求解

题目链接

http://118.190.20.162/view.page?gpid=T173

最近刚刚在上数据结构二叉树
跟这道题真的是强相关
然后在就是涉及到了数学求导
这基本上是我复学两个月做的最久的题了
感觉做完这道题对栈和二叉树理解比以前清晰了很多
不摆了
上代码

** 题目思路:
题意是给我们一个后缀表达式
显然,我们就可以写出这个表达式
那么怎么来实现求导呢?
不妨讨论,常数求导equal 0;
偏导的未知数求导equal 1 ;
其他符号 a (±)b求导equal
a导 (±
)b导
这样我们是不是就可以递归实现求导呢!
分析可得做题步骤
1:利用栈来建立二叉树存表达式,因为是后缀表示,所以唯一可以考虑只使用一个栈
2:dfs遍历求导,并将结果存在二叉树里面
3:带入计算,最重要的是要取模运算(可恶)
**

/*
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
typedef  pair<int,int> pi ;
#define if1(x) for(int i =1 ;i<=x;i++)
#define if0(x) for(int i = 0;i<x;i++)
#define jf0(x) for(int j = 0;j<x;j++)
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
int m,n,idx;
struct node{
   int vis_num;//1表示他是数字,0表示他是未知数,
   //2表示他是操作符号,3表示求偏导的未知数
   int x;
   ll val;
   char op;
   //既然要存树,那么不妨使用数组来存树
   int fa;
   int l;
   int r;
   int myself;
};
node tree[550];
//vector<node> tree;
vector<int> xs(200);
int xans;//存储偏导未知数的位置
string s;
node retu0,retux,retu1;
stack<node> sta_num;
void push_num(string x){
   int num = stoi(x);
   node te = *new(node);
   te.vis_num=1;te.val =num;
   te.fa = te.l = te.r =-1;
   tree[idx] = te;
   te.myself = idx++;
   // tree.push_back(te)
   sta_num.push(te);

}
node dfs(node root){
//分析讨论vis的三种状态
//当节点未数字时,求导直接返回0;
   if(root.vis_num==1) return retu0;//数字求导的结果就是0

//当节点遇到x时,求导时应该分两种情况判断
   else if(root.vis_num==0){
//若x是我们所求的偏导,我们就应该返回retux
       if(root.x==xans) return retu1;   
//若x不是是我们所求未知数的偏导,
//则我们应该视为常数我们就应该返回0
       else return retu0;
   }

//ok写到这里,应该是此代码最复杂的一部分
//(a*b)求导=a导*b+b导*a
   else if(root.op=='*'){
       //注意,我们还应该给新分配的三个节点一个idx来存下来
       node rnode = *new(node);
       node lnode = *new(node);
       node father = *new(node);
       // tree.push_back(rnode);
       // tree.push_back(lnode);
       // tree.push_back(father);
       //先写一下左边巴
       lnode.vis_num=2;
       lnode.op = '*';
       lnode.l = root.l;
       lnode.r = dfs(tree[root.r]).myself;
       lnode.fa = father.myself;
       tree[idx]  = lnode;
       lnode.myself = idx++;
       


       //写右边
       rnode.vis_num = 2;
       rnode.op='*';
       rnode.r = root.r;
       rnode.l = dfs(tree[root.l]).myself;
       rnode.fa = father.myself;

       tree[idx] = rnode;
       rnode.myself = idx++;
       
       //写她爹
       father.vis_num = 2;
       father.op = '+';
       father.r = rnode.myself;
       father.l = lnode.myself;
       tree[idx] = father;
       father.myself = idx++;
       return father;



   }
   //op是+-的时候,很明显就是l导+-r导
   else{
       node father = *new(node);
       father.fa = father.l = father.r = -1;
       father.vis_num=2;
       father.op = root.op;
       father.r = dfs(tree[root.r]).myself;
       father.l = dfs(tree[root.l]).myself;
       father.myself = idx;
       tree[idx++] = father;
       return father;
   }
}
void init(){
   retu0.fa = retu0.l=retu0.r=-1;
   retu0.vis_num=1;
   retu0.val=0;
   retu0.myself = -2;//特殊给0;

   retu1.fa = retu1.r=retu1.l=-1;
   retu1.vis_num=1;//特殊给一个。
   retu1.val = 1;
   retu1.myself=-4;

   retux.fa = retux.l = retux.r=-1;
   retux.vis_num = 3;
   retux.myself = -3;//特殊给未知数的下标;
   return;
}
ll caculate(node root){
   if(root.vis_num==1)return root.val;
   //遇到计算偏导x时
   else if(root.vis_num==3)return xs[xans];
   //通过处理,我们可以肯定dfs后再无vis——num=0,就是未知数的情况了
   //写在后面,根本特殊处理不了一点点
   //因为它有些是在*+-里面的未知数
   else if(root.vis_num==0) return xs[root.x];
   else{
       //现在就是开始运算了,
       ll a , b ;
       if(root.l<0){
           if(root.l == -2) a=0;
           else if(root.l == -3) a = xs[xans];
           else if(root.l==-4) a = 1;
       }
       else a = caculate(tree[root.l]);
       if(root.r<0){
           if(root.r == -2) b=0;
           else if(root.r == -3) b = xs[xans];
           else if(root.r== -4) b = 1;
       }
       else b = caculate(tree[root.r]);

       if(root.op=='+'){
           return ((a%mod)+(b%mod))%mod;
       }else if(root.op=='-'){
           return ((a%mod)-(b%mod))%mod;
       }
       else{
           return (a%mod)*(b%mod)%mod;
       }
   }
}
void solve(){
   init();
   cin>>n>>m;//n-自变量,m-求偏导的次数
   getchar();
   getline(cin,s);
   int le = s.size();
   if0(le){

       //split 操作
       int j = i+1;
       while(s[j]!=' '&&j<le)j++;
       string temp = s.substr(i,j-i);

       //为数字
       if((temp[0]>='0'&&temp[0]<='9')||temp[0]=='-'&&j>i+1){
           push_num(temp);
       }

       //为未知数的时候
       else if(temp[0]=='x'){
           int te = stoi(temp.substr(1,temp.size()-1));
           node tt = *new(node);
           tt.vis_num=0;tt.x=te;
           tt.fa = tt.l = tt.r =-1;
           tree[idx]=tt;
           tt.myself = idx++;
           //tree.push_back(tt);
           sta_num.push(tt);
       }


       else {
           //通过简单分析哈,我们在遇到操作符号的时候可以更新
           //一下树,不如直接存树?
           node te = *new(node);
           te.vis_num=2;
           te.op = temp[0];
           te.fa =-1;
           te.r = sta_num.top().myself;
           sta_num.pop();
           te.l = sta_num.top().myself;
           sta_num.pop();
           tree[idx] = te;
           te.myself = idx++;
           sta_num.push(te);

       }
       
       i=j;//1st--bug,因为for它自己已经加一辣
   }

   //存树结束
   if0(m){
       int temp_idx = idx;//考虑后面我们可以释放一波内存
       //既然写的这么认真了,那么在内存方面也可以考虑变得更优。
       cin>>xans;//读入偏导x
       jf0(n)cin>>xs[j+1];//读入x的赋值,因为我们在建树的时候
       //我们直接存的是x的下标,所以未便于调用,从1开始存。
       //简单讨论,最后栈的唯一元素便是根节点。
       node ans = dfs(sta_num.top());
       ll res = caculate(ans)%mod;
       res = (res+mod)%mod;
       cout<<res<<endl;
       idx = temp_idx;
   }

}
int main(){
   solve();
  // getchar();
   
   return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夭辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值