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]!=c就dp[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;
}

浙公网安备 33010602011771号