Codeforces Round #700 (Div. 2)(D1,D2)(贪心)

D1 - Painting the Array I

题意:
题目给出一个数组 \(a\),其中 \(1\leqslant a[i]\leqslant n\),让你把 \(a\) 分成两个数组 \(a^{(1)},a^{(2)}\),这两个数组中如果两个相邻元素相同就会合并成一个元素。问两个数组元素之和最多可以有多少个。

想法:

  • 可以对情况进行分类讨论。设 \(a^{(1)}\) 数组末尾元素为 \(list1\)\(a^{(2)}\) 数组末尾元素为 \(list2\)
  • 我们就可以对 \(a\) 数组进行遍历。
  • 第一种情况: \(list1==list2\),若当前 \(a[i]\)\(list1\) 不相等,那么直接放入 \(a^{(1)}\)即可。因为此时放在 \(a^{(1)}\)\(a^{(2)}\) 造成的影响都一样。
  • 第二种情况: \(list1!=a[i]\ and\ list2==a[i]\),那么此时 \(a[i]\) 放入 \(a^{(1)}\) ,才能让答案增加。
  • 第三种情况: \(list1==a[i]\ and\ list2!=a[i]\),那么此时 \(a[i]\) 放入 \(a^{(2)}\) ,才能让答案增加。
  • 第四种情况: \(list1!=a[i]\ and\ list2!=a[i]\),此时需要考虑选择那个对后面更优。那么就要将 \(list1\)\(a\) 数组中下一次出现的下标和 \(list2\) 下一次出现的下标进行比较。谁会更早出现,我们就在哪个数组上放入 \(a[i]\),这样就可以更大可能性的去避免出现后面的 \(a[next]\) 会和 \(list1,list2\) 相同。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100005;

int a[maxn],id[maxn];
vector<int>v[maxn];
vector<int>v1,v2;
int main()
{
    int n;
    memset(id,0,sizeof id);
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        v[a[i]].push_back(i);
    }
    int last1=-1,last2=-1,ans=0;
    for(int i=1;i<=n;i++){
        id[a[i]]++;
        if(last1==last2){
            if(last1!=a[i])last1=a[i],ans++;
        }else if(last1==a[i]&&last2!=a[i]){
            last2=a[i],ans++;
        }else if(last1!=a[i]&&last2==a[i]){
            last1=a[i],ans++;
        }else{
            if(last1==-1){
                last2=a[i];
                ans++;
            }else if(last2==-1){
                last1=a[i];
                ans++;
            }else{
                int num1=n+1,num2=n+1;
                if(id[last1]<v[last1].size())num1=v[last1][id[last1]];
                if(id[last2]<v[last2].size())num2=v[last2][id[last2]];
                if(num1<num2){
                    last1=a[i];
                    ans++;
                }else{
                    last2=a[i];
                    ans++;
                }
            }

        }
    }
    cout<<ans;
}


D1 - Painting the Array II

题意:
题目给出一个数组 \(a\),其中 \(1\leqslant a[i]\leqslant n\),让你把 \(a\) 分成两个数组 \(a^{(1)},a^{(2)}\),这两个数组中如果两个相邻元素相同就会合并成一个元素。问两个数组元素之和最少可以有多少个。

想法:

  • 和D1一样,我们同样需要结合贪心思想进行分类讨论。因为相同元素可以合并,那么让元素最少,我们要把 \(a\) 数组进行合并,把 \(a\) 合并成没有两个相邻元素相同的数组。
  • 然后我们对合并完后的 \(a\) 数组进行遍历。
  • 第一种情况: \(list1==list2\),若当前 \(a[i]\)\(list1\) 不相等,那么直接放入 \(a^{(1)}\)即可。因为此时放在 \(a^{(1)}\)\(a^{(2)}\) 造成的影响都一样。
  • 第二种情况: \(list1!=a[i]\ and\ list2==a[i]\),那么此时 \(a[i]\) 放入 \(a^{(2)}\) ,才能让答案不增加。
  • 第三种情况: \(list1==a[i]\ and\ list2!=a[i]\),那么此时 \(a[i]\) 放入 \(a^{(1)}\) ,才能让答案不增加。
  • 第四种情况: \(list1!=a[i]\ and\ list2!=a[i]\),此时需要考虑选择那个对后面更优。那么就要将 \(list1\)\(a\) 数组中下一次出现的下标和 \(list2\) 下一次出现的下标进行比较。谁会更晚出现,我们就在哪个数组上放入 \(a[i]\)。这样就使得后面的数更有可能被合并从而使答案最小化。

代码:

int a[maxn],b[maxn],id[maxn];
vector<int>v[maxn];
int main()
{
    int n,nn=1;
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    b[1]=a[1];
    v[b[1]].push_back(1);
    for(int i=2;i<=n;i++){
        if(a[i]!=a[i-1]){
            b[++nn]=a[i];
            v[b[nn]].push_back(nn);
        }
    }
    int last1=-1,last2=-1,ans=0;
    for(int i=1;i<=nn;i++){
        id[b[i]]++;
        if(last1==last2){
            if(last1!=b[i])last1=b[i],ans++;
        }else if(last1==b[i]&&last2!=b[i]){
            last1=b[i];
        }else if(last1!=b[i]&&last2==b[i]){
            last2=b[i];
        }else{
            if(last1==-1){
                last1=b[i];
                ans++;
            }else if(last2==-1){
                last2=b[i];
                ans++;
            }else{
                int num1=nn+1,num2=nn+1;
                if(id[last1]<v[last1].size())num1=v[last1][id[last1]];
                if(id[last2]<v[last2].size())num2=v[last2][id[last2]];
                if(num1<num2){
                    last2=b[i];
                    ans++;
                }else{
                    last1=b[i];
                    ans++;
                }
            }
        }
    }
    cout<<ans;
}

总结

两道题的贪心思想基本上是一致的。在使用贪心算法时,我们需要考虑的是当前对答案的贡献以及对后面答案的贡献即可,也要考虑这样才能让后面答案更优的可能性更大。

posted @ 2021-02-10 13:08  hachuochuo  阅读(80)  评论(0编辑  收藏  举报