CCF刷题计划——梯度求解(map表示公式)

参考【CCF-CSP】 202309-3 梯度求解_ccf202309-3-CSDN博客

梯度求解

计算机软件能力认证考试系统

很有启发性的一道题!这道题让我收获了如何用map表示公式:公式,说白了就是多项式。而多项式又是由众多单项式组成的,所以,我们需要先学会表达单项式。单项式形如:cX^e,需要一个值来表示系数,一个值来表示指数,对于只有一个变量的单项式,我们可以有很多种方式来表达。但是,考虑到单项式多了,有些单项式没有整合到一起,比如 3X^2+6X^2 ,相同指数的项是可以合并系数的。为了方便我们直接定位到指数的系数,我们使用 map<int,int> ,分别存储c和e,来表示一个单项式。

x1*x1*x1+x1*3转换为

指数1 系数3

指数3 系数1

在该map中添加多个c,e组合,也就意味着我们添加了多个单项式,这个map就是成为了一个多项式。

为了转化成上面的形式,我们需要提前将那些不重要的变量(也就是将那些不求导的变量)转化为系数(对于不求导的变量而言,这一步可以提前处理,也可以最后处理,都一样,为了方便,我们提前处理),然后再运算。

 AC:

#include <iostream>
#include <sstream>	//用于处理获取空格隔开的字符串 
#include <stack>
#include <string>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
#define ll long long
const int MOD=1e9+7; 
const int N=1e2+2;

/*
公式怎么表示?
	首先,将公式化简。无关的变量全部转化为系数。如x1+x1*x1*x2,求x1的倒数x2=2,那么就为x1+2*x1^2这种
	然后我们需要表示的是单独存在x1的项的系数和指数即可。推荐使用map<ll,ll>,分别表示 指数、系数  
	map外面套一层stack,便于我们对整体公式进行整合 

*/

int n,m;	//自变量个数,要求解的偏导数的个数 
vector<string>vec;
stack<map<ll,ll>>func;

int main()
{
	cin>>n>>m;
	stringstream linestream;
	string line,t;
	getchar();	//getline之前都来杯getchar 
	getline(cin,line);
	linestream.str(line);	//相当于这里将其空格处理 
	while(linestream>>t)	 vec.push_back(t);
	
	int id;
	vector<int>x(n+1,0);	//n个元素 
	while(m--)
	{
		cin>>id;
		for(int i=1;i<=n;i++)	cin>>x[i];
		string goal="x"+to_string(id);	//首先记录下了求导的变量
		for(int i=0;i<vec.size();i++)	//遍历容器 
		{
			t=vec[i];	//t可能是 运算符、xi(xi分为goal和非goal)、常数 
			//在前期,一般只会出现单个的项,如x1,x2这种,此时就是将其转换成系数的最好时机 
			
			if(t==goal)	//如果当前得到的项是要求导的项
			{
				map<ll,ll>mp;
				mp[1]=1;
				func.push(mp);	//直接保留 
			 }
			else if(t[0]=='x')	//没有进入goal的且为x,说明是非goal变量
			{
				//将其转换为系数
//				int tempId=stoi(t[1]);		//字符串转整型:stoi(string str);stol、stoll、stod(double) 
//				上面因为t[1]为char,倒不能直接转,而是先要转为string,用substr 
				int tempId=stoi(t.substr(1));	//substr(size_t pos,size_t len)
				map<ll,ll>mp;
				mp[0]=x[tempId];
				func.push(mp);
			 }
			//上面两种情况只在前期容易出现,也就是单项式的情况。下面将这些单项式逐渐转换为多项式,并进行运算 
			else if(t=="+"||t=="-"||t=="*")	//如果是运算符 
			{
				//会将栈中两个元素弹出,进行运算操作 
				//这里我们用两个map接收两个元素(其实本质是多项式) 
				map<ll,ll>mp2=func.top();	 func.pop();	 
				map<ll,ll>mp1=func.top();	 func.pop();
				map<ll,ll>mp;	//用来表示运算结果 
				
				if(t=="+")
				{
					//加法运算,如 3*x1+5*x1,相同指数的系数相加
					mp=mp1;
					for(auto &it:mp2)	//first是指数,second是系数 
						mp[it.first]=(mp[it.first]+it.second)%MOD;	//该式本质是 同指数下的mp.second+mp2.second 
						
				}
				
				else if(t=="-")	//减法同理 
				{
					mp=mp1;
					for(auto &it:mp2)	//first是指数,second是系数 
						mp[it.first]=(mp[it.first]-it.second)%MOD;
				}
				
				else
				{	//乘法就比较复杂:需要将mp1的所有项都和mp2的所有项相乘
					//而单独两项相乘,就是系数相乘,指数相加
					//就结果而言,结果的指数对应的系数就是两个系数乘后的累加	 
					for(auto &it1:mp1)
						for(auto &it2:mp2)
						{
							mp[it1.first+it2.first]+=it1.second*it2.second;
							mp[it1.first+it2.first]%=MOD; 
						}
				}
				func.push(mp);	//将运算结果多项式放回去 
			}
			else //数字
			{
				ll data=stoll(t);	//转换为longlong
				map<ll,ll>mp;
				mp[0]=data;
				func.push(mp); 
			 } 
		 } 
		
		//最后func中得到的是一个关于goal的多项式
		//接下来我们需要将这个多项式进行求导操作 
		//其实这里就简单了,指数-1,指数为0的舍弃,然后将值带入
		
		ll ans=0; 
		map<ll,ll>f=func.top();
		for(auto &it:f)
		{
			ll e=it.first;	//指数	原指数会-1 
			ll c=it.second;	//系数	原系数会*e 
			if(e==0)	continue;
			ans=(ans+c*e*(ll)pow(x[id],e-1))%MOD;
		}
		
        //下面是链接中大佬给的源代码,我不太理解,所以换成了上面的,也是满分
//		ll ans=0,pree=0,fac=1;
//		map<ll,ll>f=func.top();
//		for(auto &it:f) 
//		{
//			ll e=it.first;	//指数 
//			ll c=it.second;	//系数
//			for(int i=pree+1;i<e;i++)
//				fac=fac*x[id]%MOD;	
//			pree=e==0?0:e-1;	 
//			ans=(ans+c*e*fac)%MOD;
//		}
		cout<<(ans+MOD)%MOD<<endl;	//因为负数也要求在 [0,MOD)的范围内,所以需要+MOD使其变成正数
									//如果本来就是正数也无所谓,+mod后会%去 
	}
	return 0;
 } 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值