Think myself - 无后效性(贪心+DP)

通过对一道关于工厂生产的题目进行深入分析,作者反思了自己过往的模拟思维方式,并提出了使用贪心算法记录并传递最优成本的新视角。文章强调了多阶段决策过程中的无后效性原则,即当前状态仅与前一状态相关,而与更早或未来状态无关。

最近做了一些题,让我对之前我的思维方式产生很大的质疑。我也通过这些问题,思考我之前的思维方式,做出自己的反思,提出新的思维方式。
先通过一道题引入。简单
解决问题固然简单,关键是我们要发现背后的通性,思想的奥秘。
题目传送门
题意:
一个工厂生产一个物品,每天的生产成不尽相同,要求的产量也不尽相同。后面的产品可以在之前的时间生产,但是每天的存储费用固定为S。要你求最小的成本。
思路:
贪心记录最小的价格。

AC code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<cstring>
#include<string>
#include<cmath>

using namespace std;

typedef long long LL;

#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define mem(a,b) memset(a,b,sizeof(a))
#define per(i,a,b) for(int i = a;i <= b;++i)
#define rep(i,a,b) for(int i = a;i >= b;--i)
const int maxn = 3e5;
int n = 0,s = 0;

int main(){
	while(~scanf("%d %d",&n,&s)){
		int minc = 1e4 + 10;
		LL ans = 0;
		per(i,1,n){
			int c = 0,y = 0;
			scanf("%d %d",&c,&y);
			minc = min(minc + s,c);
			ans += minc * y;
		}
		printf("%I64d\n",ans);
	}
	
	return 0;
}

这道题就是贪心。
这道题不算是多阶段决策最优化问题,也就是DP类型。因为当前的选择与之前所有的选择都没有关系。典型的贪心。
我之前一直卡壳,就是我思考的是如何从当前位置去往前面找到最优的填数,然后在那天生产。
这是典型的模拟思维方式。我总是用模拟的思维做想问题的解法。这种思维太常规,而且经常要考虑各种问题,实现复杂。
为什么你一定要找到那个最优的日期,然后在那天生产呢?为什么不记录传递下来的最优价格呢?找到最优的日期很麻烦,但是将最优的价格传递下去缺失十分方便的,这样我们就不用考虑那些复杂的过程了。

最近一直在看DP,有了很多新收获,也刷新了以往的认知。所以有些内容你回过头来再次阅读思考一遍,就会有新的收获。
多阶段决策过程中
无后效性:当前的S状态只与前一个S-1状态相关,也就是说,S状态只由S-1状态通过状态转移方程转移过来,而与更前面的状态无关,还有后面没有发生的状态。
这句话刷新了我之前的认识。
这句话指出了我之前思维方式的重大问题。
我考虑当前的S状态时,我总是喜欢考虑之前所有的状态,甚至之后的状态。导致问题很复杂。

<think>嗯,用户问的是关于C语言中的贪心策略如何解析像+-3、-+-3、---n、-(-(--n))这样的表达式。首先,我需要回忆一下C语言的词法分析规则,特别是贪心策略(最长匹配原则)的应用。 贪心策略指的是编译器在解析符号时,总是尽可能多地匹配字符组成一个运算符。比如,当遇到连续的++时,会被解析为自增运算符,而不是两个+号。同样,对于类似的情况,比如用户提到的表达式,需要按照这个原则来分解。 首先看第一个例子:int m8 = +-3; 这里用户说贪心策略是+正号,-负号。但实际上,按照贪心规则,编译器会如何处理呢?可能需要拆解符号序列。比如,这里连续的+-,编译器会尝试最长匹配。但C语言中并没有+=这样的操作符出现在这里,所以可能应该分开处理。或者,可能这里的贪心策略是指如何分解连续的符号? 比如,在表达式中,编译器会尽可能多地读取字符组成一个标记。例如,在---n中,编译器会优先匹配--作为自减运算符,剩下的-作为负号。所以---n会被解析为---n。那么表达式等价于先对n进行自减,然后取n的负数。但需要注意的是,自减运算符是前置还是后置的问题,这里如果是前置的话,表达式是--n,然后剩下的-号是负号。所以整个表达式相当于-(--n)。但用户给出的例子是---n被解析为--自减和-n,也就是相当于(--n)的负数吗?或者可能解析为-(-n)?这需要更仔细的分析。 接下来第二个例子:int m9 = -+-3; 用户说贪心策略是-负号,+正号,-负号。这里可能的解析是,编译器如何分解-+-。按照最长匹配原则,第一个-作为负号,然后+作为正号,剩下的-作为负号,所以整个表达式是-+-3))。或者是否有其他可能的解析方式?比如,是否存在可能的运算符组合,例如-+是否会被视为一个整体?但C语言中没有这样的运算符,所以应该分开处理。所以-+-3相当于-+-3)),即-(-3) = 3,然后加上前面的负号是-3?或者可能顺序不同? 第三个例子:int m10 = ---n; 用户说是--自减,-n。这里,三个连续的减号会被解析为--(自减运算符)和-(负号)。例如,---n会被解析为(--n) - n?或者可能解析为- (--n)?或者更可能的是,解析为---n?或者可能编译器如何处理? 第四个例子:int m11 = -(-(--n)); 这里用户可能想说明括号改变了运算顺序,但需要确认是否正确。 我需要逐一分析这些表达式,应用贪心策略来分解符号,并理解它们的实际含义。 首先,关于贪心策略的具体应用:在词法分析阶段,编译器会尽可能多地读取字符来形成一个有效的标记。例如,当遇到连续的减号时,会优先匹配为自减运算符(--),而不是单独的减号。同样的规则适用于其他可能的运算符组合。 现在具体分析每个例子: 1. int m8 = +-3; 这里连续的+-。根据贪心策略,编译器会先读取+,然后看下一个字符是否可以组成更长的运算符,比如+=,但这里后面是-,所以+单独作为正号,然后-作为负号。所以整个表达式是+(-3),即-3。但C语言中允许正号吗?是的,一元+运算符,所以+3是3,而+(-3)等于-3。所以m8被赋值为-3。 2. int m9 = -+-3; 这里有三个连续的符号:-+-。编译器如何解析?首先读取第一个-,然后看后面是否有连续的字符组成更长的运算符。比如,-+是否是可能的?C中没有这样的运算符,所以第一个-作为负号,然后+作为正号,最后-作为负号。所以表达式是-+-3))= -(-3) = 3。 3. int m10 = ---n; 三个减号。编译器会先尝试最长匹配,前两个减号组成自减运算符--,剩下的第三个减号作为负号。所以表达式解析为---n。但自减运算符需要作用于变量,所以这里应该是前置自减:--n,然后剩下的-作为负号。所以整个表达式是 -(--n)。这样,n先自减1,然后取负数。例如,假设n初始为5,执行后m10的值是 -(5-1) = -4?或者如果n是5,自减后变为4,然后取负数是-4。但需要注意的是,自减运算符的副作用和表达式求值的顺序。 4. int m11 = -(-(--n)); 这里括号改变了运算顺序。先计算--n,然后两次负号,相当于原值。例如,假设n初始为5,--n变为4,然后两次负号变为4。所以m11的值是4。但用户可能想说明括号内的运算顺序,而原表达式中的结构。 需要验证这些解析是否正确。例如,在C语言中,自减运算符的优先级高于负号,所以前置自减的优先级较高。但表达式中的符号如何结合,需要考虑运算符的优先级和结合。 对于第三个例子,---n的解析可能有两种情况: 情况一:--n作为前置自减,然后负号作用于结果。即 -(--n) 情况二:-(-n) 其中两个减号作为负号,但根据贪心策略,前两个减号会被解析为自减运算符,因此情况一更可能正确。 现在需要确认这些解析是否正确,可以通过编写测试代码来验证。 例如: 测试m8 = +-3的情况: int m8 = +-3; printf("%d\n", m8); // 应该输出-3 测试m9 = -+-3: int m9 = -+-3; printf("%d\n", m9); // 输出3 测试m10 = ---n的情况: int n = 5; int m10 = ---n; printf("%d %d\n", m10, n); // 假设解析为--n然后取负数,n先减到4,然后m10是-4。输出-4 4 测试m11 = -(-(--n)): int n =5; int m11 = -(-(--n)); printf("%d %d\n", m11, n); // --n后n=4,然后两次负号,结果4。输出4 4 但需要确保这些结果是否符合C标准。 此外,用户提到的“贪心策略”在词法分析阶段的作用,即如何将字符序列分割成标记(tokens)。例如,在解析时,编译器会尽可能多地读取字符以形成一个有效的标记。例如: 在解析---n时,前两个减号组成--(自减运算符),剩下的-和n无关,所以整个表达式是--n的前置自减,然后外面的负号?或者原式是---n,可能被解析为 (--n) -n?这显然不可能,因为中间没有运算符。或者更可能的是,前两个减号组成自减运算符,第三个作为负号,所以整个表达式是 -(--n)。这种情况下,表达式的结果是负的(前置自减后的n)。 但需要确认,C语言中是否允许这样的解析。例如,表达式中的每个标记必须正确结合。例如,前置自减运算符的语法是:++表达式 或 --表达式。因此,在---n的情况下,解析为 --n,然后前面的-号作为负号,所以整体是 -(--n)。 现在,用户可能对这些表达式的解析方式感到困惑,特别是连续符号的组合。需要详细解释每个例子的解析过程,并说明贪心策略如何应用。 总结每个例子的解析: 1. +-3 → +(-3) → 由于一元+运算符的作用,但+(-3)等于-3。 2. -+-3 → -(+(-3))-(-3) = 3。 3. ---n → -(--n) → 先对n进行前置自减,然后取负数。 4. -(-(--n)) → 括号明确优先级,先执行--n,然后两次负号抵消。 用户可能希望了解如何正确解析这些连续的运算符符号,以及贪心策略如何影响解析结果。此外,可能还需要指出在实际编程中应避免这种模糊的表达式,以提高代码可读。 </think>### 一、贪心策略与符号解析规则 #### **1. 贪心策略(Maximal Munch)** - **定义**:编译器在词法分析阶段,总是尽可能多地读取字符组成一个合法的标记(token)。 - **示例**: - `+-` 会被拆分为 `+` 和 `-`(无 `+=` 或 `++` 的可能)。 - `--` 会被解析为自减运算符(`--`),而非两个 `-`。 #### **2. 一元运算符优先级** - **优先级顺序**:自增/自减(`++`/`--`) > 正负号(`+`/`-`)。 - **结合**:一元运算符为右结合(从右向左结合)。 --- ### 二、逐个解析表达式 #### **1. `int m8 = +-3;`** - **解析过程**: - 贪心策略优先匹配 `+` 和 `-` 为独立符号(无 `++` 或 `+=` 的可能)。 - 等价于 `+(-3)`。 - **结果**: - `+(-3)` → 一元正号无影响,结果为 `-3`。 - **验证代码**: ```c int m8 = +-3; printf("%d\n", m8); // 输出 -3 ``` --- #### **2. `int m9 = -+-3;`** - **解析过程**: - 贪心策略拆分为 `-`、`+`、`-`。 - 等价于 `-(+(-3))`。 - **结果**: - `+(-3)` → `-3`,再取负号 → `3`。 - **验证代码**: ```c int m9 = -+-3; printf("%d\n", m9); // 输出 3 ``` --- #### **3. `int m10 = ---n;`** - **解析过程**: - 贪心策略优先匹配 `--` 为自减运算符,剩余 `-` 为负号。 - 等价于 `-(--n)`(先自减,再取负)。 - **结果**: - 假设 `n = 5`: - `--n` → `n = 4`。 - `-(--n)` → `-4`。 - **验证代码**: ```c int n = 5; int m10 = ---n; printf("m10 = %d, n = %d\n", m10, n); // 输出 m10 = -4, n = 4 ``` --- #### **4. `int m11 = -(-(--n));`** - **解析过程**: - 括号强制优先级:先解析 `--n`,再两次负号抵消。 - 等价于 `--n` 的值。 - **结果**: - 假设 `n = 5`: - `--n` → `n = 4`。 - `-(-4)` → `4`。 - **验证代码**: ```c int n = 5; int m11 = -(-(--n)); printf("m11 = %d, n = %d\n", m11, n); // 输出 m11 = 4, n = 4 ``` --- ### 三、关键规则与注意事项 | **规则** | **说明** | |-------------------------|--------------------------------------------------------------------------| | 贪心策略 | 优先匹配最长的合法符号(如 `--` 优先于 `- -`)。 | | 一元运算符结合 | 从右向左结合(如 `- - 3` 等价于 `+3`)。 | | 自增/自减副作用 | 修改变量值,需注意副作用发生顺序(如 `---n` 先修改 `n`)。 | | 避免歧义表达式 | 实际开发中应使用括号明确优先级,避免连续符号导致可读问题。 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值