记录大佬的思路
洛谷染色问题,即2024CSP-S第三题。
原题复述:
给定一个长度为 n 的正整数数组 A,其中所有数从左至右排成一排。你需要将 A 中的每个数染成红色或蓝色之一,然后按如下方式计算最终得分:
设 C 为长度为 n 的整数数组,对于 A 中的每个数 左侧没有与其同色的数,则令 Ci=0。否则,记其左侧与其最靠近的同色数为 Aj ,若 Ai=Aj,则令 Ci=Ai
否则Ci为0。最终得分为 C 中所有整数的和,即你需要最大化最终得分,请求出最终得分的最大值。
一开始考虑肯定是简单的搜索,很可惜,没有重复子问题,所以记忆化搜索用不上。暴搜显然会寄,测试通过前四个点。
#include<bits/stdc++.h>
using namespace std;
#define max(a,b) (a>b?a:b)
int t,n;int a[200001];int ans[20001];int ind=0;
map<int,int>m;
int s(int ind,int stat)
{
if(ind==n)
{
//cout<<bitset<6>(stat)<<endl;
/*if(m.find(stat)!=m.end())
return m[stat];*/
int ans=0;
for(int i=1;i<n;i++)
{
int s=(stat&(1<<i))>>i;//
int c=0;
for(int j=i-1;j>=0;j--)
if((s==((stat&(1<<j))>>j)))
{
c=a[i]==a[j]?a[i]:0;
break;
}
//cout<<c<<endl;
ans+=c;
}
//cout<<bitset<9>(stat)<<" "<<ans<<endl;
//m[stat]=ans;
return ans;
}
int a=s(ind+1,(stat|(1<<ind)));
int b=s(ind+1,stat);
int ans=max(a,b);
return ans;
}
int main()
{
cin>>t;
while(t--)
{
cin>>n;
memset(a,0,sizeof(a));
for(int i=0;i<n;i++)
cin>>a[i];
ans[ind++]=s(0,0);
}
for(int i=0;i<ind;i++)
cout<<ans[i]<<endl;
return 0;
}
搜索用不上,下面就要考虑规划,以下是题解大佬的动态规划代码。每次f[i]只会在a[i]已经出现过时产生更新。以s记录相邻的a[i]元素产生的增益。选择lst[a[i]]+1的原因是lst[a[i]]+1可能与lst[a[i]]之前的某点产生增益(之后的增益均被s记录)。
#include <bits/stdc++.h>
#define int long long
#define rint register int
#define endl '\n'
#define m(a) memset(a, 0, sizeof a)
using namespace std;
const int N = 1e6 + 5;
int n, T;
int a[N], lst[N], f[N];
int s[N], ans;
signed main()
{
cin >> T;
while (T--)
{
cin >> n;
m(a), m(lst), m(f), m(s);
for (rint i = 1; i <= n; i++) cin >> a[i];
for (rint i = 2; i <= n; i++) s[i] = (a[i] == a[i - 1] ? s[i - 1] + a[i] : s[i - 1]);
for (rint i = 1; i <= n; i++)
{
f[i] = f[i - 1];
if (lst[a[i]]) f[i] = max(f[i], f[lst[a[i]]+1] + a[i] + s[i] - s[lst[a[i]]+1]);
lst[a[i]] = i;
}
cout << f[n] << endl;
}
return 0;
}