Live2D

P17200 「奶龙OI」Round 1 - 彩带拼接

题意

\(n\) 条彩带中按原顺序选若干条,拼成总长恰好为 \(L\) 的长彩带。每条彩带有一个美观度 \(v_{i}\) 长度 \(l_{i}\) 编号 \(c_{i}\)

规则:

  • 必须按编号 \(1 \sim n\) 的顺序选(子序列)
  • 相邻两条颜色不能相同
  • 第一条颜色无限制

输出: 最大美观度;无解输出 \(-1\)

思路

显然这道题如果没有颜色限制就是一道 01背包 板子题。
我们需要加入颜色的判定 (这是一句废话)
一开始我根据题意就想到开一个 三维dp ,dp[i][j][c] = 考虑前 \(i\) 条彩带,总长度恰好为 \(j\),且最后一条选的颜色为 \(c\) 时的最大美观度。
转移:

  • 不选第 \(i\) 条:dp[i][j][c]=max(dp[i][j][c],dp[i-1][j][c])
  • 选第 \(i\) 条(要求颜色不同):若 c[i]!=cdp[i][j][c[i]]=max(dp[i][j][c[i]],dp[i-1][j-l[i]][c]+v[i])

输出 max(dp[n][L][c]) 就可以了
完结撒花
非常明显这 会超时,也会爆内存

思路二号

显然我们需要优化,我们需要压维,也要处理颜色重复的问题。
这是聪慧的你肯定可以想到可以选取多个解,第一个解颜色重复了就拿第二个。显然为了最后结果最大我们选的肯定是最大解,次大解...
非常显然我们只需要两个就够了,一个最大,一个次大。
然后就是考虑压维,显然 \(i\)\(c\) 是不需要,以下是详细解释(并不详细):

  • \(i\) 条彩带只会影响它自己选或不选,不会影响其他彩带的状态。
  • 然后就是 \(c\) 其实这是显然的 \(c_{i}\) 最大打掉了 \(10^{9}\) 显然不能包括在复杂度里面,所以正如上面所说,只保留最优 + 次优两个值。

最后的 dp[][]
· dp[j][0]:长度为 \(j\) 时的最大美观度,以及对应的结尾颜色
· dp[j][1]:长度为 \(j\) 时,结尾颜色与 dp[j][0] 不同的次大美观度,以及对应的结尾颜色

显然我们还有一个数组来存结尾颜色,就叫做 col

转移懒得写了直接看代码吧

代码环节

一开始边界处理错了

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N=1e3+5; 
const int M=1e5+5; 
const ll INF=-4e18;  
int n,L;                
int l[N],v[N],c[N];    

// dp[j][0]: 长度为j时的最大美观度
// dp[j][1]: 长度为j时,结尾颜色与dp[j][0]不同的次大美观度
ll dp[M][2];

// col[j][0]: dp[j][0]对应的结尾颜色
// col[j][1]: dp[j][1]对应的结尾颜色
int col[M][2];

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	cin>>n>>L;
	for(int i=1;i<=n;i++) cin>>l[i]>>v[i]>>c[i];

	// 初始化所有长度为j的状态为不可达
	// col初始化为-1,表示没有结尾颜色(还没有选任何彩带)
	for(int j=0;j<=L;j++)
	{
		dp[j][0]=dp[j][1]=INF;
		col[j][0]=col[j][1]=-1;
	}

	dp[0][0]=0;
	col[0][0]=-1;

	for(int i=1;i<=n;i++)
	{
		// 倒序枚举!!!
		for(int j=L;j>=l[i];j--)
		{
			int pre=j-l[i];

			// 从pre转移到j,需要判断颜色是否冲突
			// 如果pre的最优解颜色不等于c[i],可以直接用最优解
			// 否则只能用次优解(颜色与最优解不同,必然不等于c[i])
			ll val;
			if(col[pre][0]!=c[i])
			{
				if(dp[pre][0]<=INF) continue;
				val=dp[pre][0]+v[i];
			}
			else
			{
				if(dp[pre][1]<=INF) continue;
				val=dp[pre][1]+v[i];
			}

			// 用候选值val更新dp[j][0]和dp[j][1]
			// 分三种情况讨论:

			// 情况1:候选值颜色 == 当前最优解颜色
			// 只能尝试更新dp[j][0](不能更新次优,因为次优要求颜色与最优不同)
			if(col[j][0]==c[i])
			{
				if(val>dp[j][0]) dp[j][0]=val;
			}
			// 情况2:候选值颜色 == 当前次优解颜色
			// 尝试更新dp[j][1],如果超过dp[j][0]则交换
			else if(col[j][1]==c[i])
			{
				if(val>dp[j][1])
				{
					dp[j][1]=val;
					// 如果次优超过了最优,交换两者
					if(dp[j][1]>dp[j][0])
					{
						swap(dp[j][0],dp[j][1]);
						swap(col[j][0],col[j][1]);
					}
				}
			}
			// 情况3:候选值颜色与最优、次优都不同
			// 按大小插入到合适位置
			else
			{
				if(val>dp[j][0])
				{
					// 超过最优,原最优降为次优
					dp[j][1]=dp[j][0];
					col[j][1]=col[j][0];
					dp[j][0]=val;
					col[j][0]=c[i];
				}
				else if(val>dp[j][1])
				{
					// 介于最优和次优之间,更新次优
					dp[j][1]=val;
					col[j][1]=c[i];
				}
			}
		}
	}

	if(dp[L][0]<=INF) cout<<-1<<"\n";
	else cout<<dp[L][0]<<"\n";

	return 0;
}
posted @ 2026-09-20 21:39  小王小王i  阅读(3)  评论(0)    收藏  举报
Live2D