小D的剧场(思维dp)

部署运行你感兴趣的模型镜像

链接:https://ac.nowcoder.com/acm/contest/369/A
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

若你摘得小的星星 你将得到小的幸福 

若你摘得大的星星 你将得到大的财富 

若两者都能摘得 你将得到永远的愿望 

摘星是罪孽的宽恕 摘星是夜晚的奇迹 

抓住它吧 你所期望的那颗星

 

无法触及,因而耀眼  

明明触及了,却还是耀眼

——《少女☆歌剧 Revue·Starlight》

题目描述

"我明白。"

作为这命运剧场永远的观众,小D一直注视着这片星光璀璨的舞台,舞台上,少女们的身姿演绎出了一幕幕动人的场景,令人回味无穷。

有的时候,小D也会自己写一些歌曲,来加入Starlight的剧本,使得剧本充满了新的生命力。

现在小D又要准备写乐谱了,小D写谱的方式比较独特。他会先写出一个按照音符出现顺序排成的序列,再进一步整合,每次整合会选取相邻的三个作为三和弦。整合次数无限。

小D选取的音符形如D5 F6这种形式,例如D5表示D大调sol(这里不考虑升降音)为了方便生成乐谱,他将这些音符进一步转化了,小D给C D E F G A B重新编号成了1 2 3 4 5 6 7,之后新的音符编号生成方式应为(字母对应的标号-1)*7+数字,例如C7=(1−1)×7+7=7

但小D讨厌一些他所认为的不优美的和弦,因此他并不希望自己的谱子里面有可能出现这样的三和弦,也就说音符组成的序列里不应该存在他所讨厌的子段,假如C5 F1 A2这三个音符凑成的和弦小D不喜欢,那么序列里面就不能出现C5 F1 A2,C5 A2 F1,A2 C5 F1,A2 F1 C5,F1 A2 C5,F1 C5 A2这六种子段。

现在小D正在推算有多少合法的序列,答案对 10^9+7 取模。

星屑飘洒的舞台上,可人绽放的爱之花,请努力让大家星光闪耀吧!

输入描述:

第一行为两个整数 n, q ,表示序列的长度和有多少和弦小D不喜欢.
接下来 q 行,每行三个整数 a, b, c ,表示小D不想出现的和弦

输出描述:

一行一个整数,表示答案

示例1

输入

复制

10 10
18 3 3
43 28 22
42 28 3
48 48 4
29 9 31
47 9 22
1 22 49
15 48 29
2 8 27
4 24 34

输出

复制

382785822

示例2

输入

复制

3 1
1 2 3

输出

复制

117643

说明

 

一共有6种不合法的序列:

1 2 3

1 3 2

2 1 3

2 3 1

3 1 2

3 2 1

答案为49^3−6=117643

备注:

3≤n≤500,0≤q≤117649,1≤a,b,c≤49

题意:求一个序列中不含有不合法子序列的个数。

题解:排列组合,用dp解决,dp[i][r][j]的意思是:i为序列长度,r为序列倒数第二位,j为序列倒数第一位,a[][][]=1代表不合法,a[][][]=0代表合法,然后我们就可以推出递推式 dp[i][r][j]=dp[i][r][j]+(a[j[k][r]^1)*dp[i-1][k][r];异或是因为0表示合法,1表示不合法~~这样就可以写代码了,上代码(代码里还有细节解释):

#include <iostream>
#include <cstring>
using namespace std;
const int MAX = 666;
const int mod = 1e9+7;
int dp[MAX][66][66];
int a[MAX][66][66];
int main(){
	int n,q;
	cin >> n >> q;
	dp[0][0][0]=1;//注意初始化为1
	for (int i = 0; i < q;i++){
		int x,y,z;
		cin >> x >> y >> z;
		a[x][y][z]=a[x][z][y]=a[y][x][z]=a[y][z][x]=a[z][x][y]=a[z][y][x]=1;//不合法标记为1
	}
	for (int i = 1; i <= n;i++){
		for (int j = 1; j <= 49;j++){
			for (int k = 0; k <= 49;k++){
				for (int r = 0; r <= 49;r++){
					dp[i][r][j]=(dp[i][r][j]+(a[j][k][r]^1)*dp[i-1][k][r])%mod;//递推式
				}
			}
		}
	}
	int ans=0;
	for (int i = 1; i <= 49;i++){
		for (int j = 1; j <= 49;j++){
			ans=(ans+dp[n][i][j])%mod;//最后两位的各种情况求和,因为没有带上最后两位进行dp求和,只求出最后两位每个情况有多少种,所以需要求和~~
		}
	}
	cout << ans << endl;
	return 0;
}

 

您可能感兴趣的与本文相关的镜像

Seed-Coder-8B-Base

Seed-Coder-8B-Base

文本生成
Seed-Coder

Seed-Coder是一个功能强大、透明、参数高效的 8B 级开源代码模型系列,包括基础变体、指导变体和推理变体,由字节团队开源

<think>好的,我现在要回答用户的问题:“能用贪心思维的题是不是都能用dp来解”。首先,我需要理解这两个概念的关系以及它们的适用范围。 用户之前已经询问过动态规划的核心思想,现在他们想知道贪心算法的问题是否都可以用动态规划来解决。这可能涉及到两种算法的区别和联系。我的目标是准确解释两者的关系,避免误解。 首先,动态规划(DP)和贪心算法都是解决最优化问题的方法,但它们的应用条件不同。动态规划通常用于具有最优子结构和重叠子问题的问题,而贪心算法则适用于具有贪心选择性质的问题,即局部最优选择能导致全局最优解。 接下来,我需要考虑用户可能的疑问。用户可能认为,因为DP更通用,所以能解决贪心的问题。但实际情况是,贪心算法适用的题目虽然可以用DP来解,但可能并不高效,甚至可能因为状态设计复杂而不必要。 我需要举例说明,例如经典的贪心问题如活动选择问题或霍夫曼编码,虽然可以用DP,但贪心更为高效。同时,有些问题只能用DP,如背包问题的某些变种,而贪心无法解决。 另外,要注意区分哪些问题只能用贪心,哪些只能用DP。例如,当问题具有贪心选择性质时,贪心算法通常是更优的选择,而如果问题不具备该性质,则需要用DP。 可能存在的误区是认为DP可以替代所有贪心问题,但实际上虽然技术上可能,但效率可能低下。需要指出这一点,并说明为什么贪心在特定情况下更优。 最后,总结两者的关系和适用场景,帮助用户明确在何种情况下选择哪种方法。</think>### **一、本质关系分析** **结论**:理论上可行,但**实际价值有限**。所有贪心算法适用的题目都可以用动态规划求解,但**逆向不成立**。关键在于两种方法的核心要求不同: | **维度** | **贪心算法** | **动态规划** | |----------------|---------------------------------------|-----------------------------| | **核心要求** | 必须满足**贪心选择性质** | 需要**最优子结构+重叠子问题** | | **计算效率** | 通常 $O(n)$ 或 $O(n \log n)$ | 一般 $O(n^2)$ 或更高 | | **解的正确性** | 依赖严格数学证明 | 天然保证全局最优解 | | **典型场景** | 活动选择、霍夫曼编码、最小生成树 | 背包问题、最短路径、编辑距离 | --- ### **二、技术可行性论证** #### **案例1:活动选择问题** - **贪心解法**:按结束时间排序后选择最早结束的活动 ```python def select_activities(intervals): sorted_intervals = sorted(intervals, key=lambda x: x[1]) res = [sorted_intervals[0]] for interval in sorted_intervals[1:]: if interval[0] >= res[-1][1]: res.append(interval) return res ``` - **DP实现**: $$dp[i] = \max(dp[j] + 1) \quad \text{对所有} j < i \text{且活动}j\text{不与}i\text{冲突}$$ **时间复杂度**:$O(n^2)$ ➔ 效率显著低于贪心的 $O(n \log n)$ #### **案例2:找零钱问题(硬币无限)** - **贪心适用条件**:硬币面值满足**规范体系**(如美元1,5,10,25) ```python def coinChangeGreedy(coins, amount): coins.sort(reverse=True) count = 0 for coin in coins: while amount >= coin: amount -= coin count += 1 return count if amount == 0 else -1 ``` - **DP强制实现**: $$dp[i] = \min(dp[i - coin_j] + 1) \quad \text{对所有} coin_j \leq i$$ **时间复杂度**:$O(n \cdot k)$($k$为硬币种类数)➔ 在非规范货币体系下必须使用DP --- ### **三、关键差异对比** | **对比维度** | **贪心算法** | **动态规划** | |--------------------|---------------------------------------|-----------------------------| | **计算方向** | 单向推进,无回溯 | 需要考虑所有可能状态转移 | | **空间消耗** | 通常 $O(1)$ | 至少 $O(n)$ 级别存储空间 | | **设计复杂度** | 依赖观察构造策略 | 需要明确状态定义与转移方程 | | **适用范围** | 窄(需严格满足贪心性质) | 广(满足最优子结构即可) | --- ### **四、实践指导原则** 1. **优先验证贪心性质** - 尝试用交换论证法证明:假设存在更优解,通过替换元素是否矛盾 - 例:任务调度问题中,贪心选结束时间早的活动无法被其他选择超越 2. **DP作为保底方案** - 当无法证明贪心策略正确性时,改用动态规划 - 典型场景:LeetCode 322.零钱兑换(硬币面值任意时) 3. **混合使用策略** - **预处理阶段**使用贪心缩小问题规模 - **核心计算层**使用DP确保正确性 ```python # 混合算法示例:带剪枝的背包问题 def knapsack_hybrid(items, capacity): # 先按价值密度贪心排序 items.sort(key=lambda x: x.value/x.weight, reverse=True) # 再用DP计算精确解(但搜索空间已减少) return dp_implementation(items, capacity) ``` --- ### **五、典型反例说明** #### **反例1:非规范货币体系** - 硬币面值:$[1,3,4]$,目标金额:6 - **贪心输出**:4+1+1(3枚) - **最优解**:3+3(2枚) - **必须使用DP**才能得到正确结果 #### **反例2:带权区间调度** - 每个活动有不同权重,需最大化总权重 - 贪心按结束时间选择会失败 - 必须使用DP: $$dp[i] = \max(dp[i-1], dp[prev(i)] + weight_i)$$ --- ### **六、选择决策树** ```mermaid graph TD A{问题是否具有贪心选择性质?} A -->|是| B[使用贪心算法] A -->|否| C[检查是否满足最优子结构] C -->|是| D[采用动态规划] C -->|否| E[考虑回溯/分支限界等其他方法] ``` --- ### **七、总结** - **能但不建议**:用DP解贪心问题会牺牲效率,但某些场景下可结合使用 - **核心区别**:贪心算法的局部最优决策不可逆,DP会保留所有可能性 - **实践建议**:先尝试证明贪心策略,若失败再转向动态规划设计
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

心脏dance

如果解决了您的疑惑,谢谢打赏呦

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

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

打赏作者

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

抵扣说明:

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

余额充值