Codeforces Round #744(Div.3)(A~E2)

本文探讨了四个编程挑战:字符串抵消游戏(思维)、数组排序的优化算法(暴力+思维)、V形构造检查(暴力+思维)和高效会议操作(贪心+思维+优先队列)。通过实例解析展示了如何利用双端队列、离散化和树状数组来解决这些问题。
摘要由CSDN通过智能技术生成

目录

A - Casimir’s String Solitaire(思维+水题)

B - Shifting Sort (暴力)

C - Ticks (暴力+思维)

D - Productive Meeting(贪心+思维+优先队列)

E1. Permutation Minimization by Deque(双端队列+水题)

E2. Array Optimization by Deque(贪心+离散化+树状数组)


A - Casimir’s String Solitaire(思维+水题)

题意:

给出一个仅有字母A、B、C组成的字符串。A和B可抵消,B和C可抵消。

问是否可以得到空的字符串。

思路:

统计A,B,C字符的数量,判断B是否等于A+C

AC代码:

B - Shifting Sort (暴力)

题意:

给定一个长度为n的数字序列,通过规则把序列变得有序,输出操作次数与每次的操作方式,不需要求最小的操作次数。规则:选择区间[l,r],将其中的数都左移d个单位。

 思路:

数组有序即最后每个数字都有自己该在的位置,遍历n个位置,每次都将i位置的正确数找到。在区间i~n中选择最小的放在i位置上,下次再在区间i+1~n中找最小,确定i+1位置上的数。每次的偏移量d=正确数的下标-i(查询最小值区间的起始下标)。

WA的原因:固定每次偏移量为1,一步一步的移动,移动次数太多了。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=1e5+10;
vector<int>a;
int c[60];
struct node
{
    int l,r,d;
}w[1000];
int main()
{
    int t,n,x;
    cin>>t;
    while(t--)
    {
       cin>>n;
       a.clear();
       for(int i=1;i<=n;i++)
       {
           cin>>x;
           a.push_back(x);
       }
       for(int i=0;i<n;i++)
       {
           w[i].l=0;
           w[i].r=0;
           w[i].d=0;
       }
      // sort(c+1,c+1+n);
       int p=0;
       int cnt=0;
       int id=0;
       int k=0;
       int tt;
       while(p<n)
       {
            int minn=INT_MAX;
            for(int i=p;i<n;i++)
            {
                if(minn>a[i])
                {
                    minn=a[i];
                    id=i;
                }
            }
           
            if(id>p)
            {
                w[k].l=p+1;
                w[k].r=id+1;
                w[k].d=id-p;
                tt=a[id];
                a.erase(a.begin()+id);
                a.insert(a.begin()+p,tt);
                k++;
                p++;
         
            }
            else
            {
                p++;
                //continue;
            }

       }
       cout<<k<<endl;
       for(int i=0;i<k;i++)
       {
           cout<<w[i].l<<" "<<w[i].r<<" "<<w[i].d<<endl;
       }
    }
}

C - Ticks (暴力+思维)

题意:

给出一个n*m的字符矩阵,判断该矩阵的*是否都用于构成至少为k的V形。

在第二个样例中,可以由大小为2的V和大小为3的V组成。

 

思路:

暴力,对于每个*让它作为V型的底端判断它的d是否大于k,如果大于则将该V型的每个点标记,最后判断被标记的点是否等于*的总数。

AC代码:

#include<bits/stdc++.h>
using namespace std;
char a[30][30];
int vis[30][30];
int t,n,m,k;
void check(int x,int y)
{
    int d=0,c=0;
    for(int i=x-1;i>=1;i--)
    {
        c++;
        if((y-c)<0||(y+c)>m)
            break;
        if(a[i][y-c]=='*'&&a[i][y+c]=='*')
        {
            d++;
            //continue;
        }
        else
        {
            break;
        }
    }
    if(d>=k)
      {
          vis[x][y]=1;
          for(int i=1;i<=d;i++)
          {
              vis[x-i][y+i]=1;
              vis[x-i][y-i]=1;
          }
      }
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>k;
        int sum=0;
        int s=0;
        bool f;
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                cin>>a[i][j];
            }
        }
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                if(a[i][j]=='*')
                {
                    sum+=1;
                    check(i,j);
                }
            }
        }
         for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                if(vis[i][j]==1)
                {
                   s++;
                }
            }
        }
        if(sum==s)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    }
}

D - Productive Meeting(贪心+思维+优先队列)

题意:

给定有n个数,每次选择两个不同的数,对它们各减一,使得你减的操作次数尽可能的多,输出操作的方案数及其方案。

思路:

一个优先队列,大根堆,每次从堆顶取两个元素,最大值和次大值,各自减1后再放回队列。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=2e5+10;
priority_queue<pair<int,int>> q;
pair<int,int>a[maxx];
pair<int,int>p[maxx];
int main()
{
    int t,n;
    cin>>t;
    while(t--)
    {
       while(!q.empty())
          q.pop();
       cin>>n;
       for(int i=1;i<=n;i++)
       {
          cin>>a[i].first;
          a[i].second=i;
          if(a[i].first>0)
          {
              q.push(a[i]);
          }
       }
       int c=0;
       pair<int,int>p1,p2;
       while(q.size()>1)
       {
           p1=q.top();q.pop();
           p2=q.top();q.pop();
           p[c].first=p1.second;
           p[c].second=p2.second;
           c++;
           if(--p1.first)
               q.push(make_pair(p1.first,p1.second));
            if(--p2.first)
               q.push(make_pair(p2.first,p2.second));
       }
       cout<<c<<endl;
       for(int i=0;i<c;i++)
       {
           if(p[i].first>p[i].second)
            swap(p[i].first,p[i].second);
           cout<<p[i].first<<" "<<p[i].second<<endl;
       }
    }
}

E1. Permutation Minimization by Deque(双端队列+水题)

题意:

给一个长度为n的序列A,现有一个空的双端队列B,将A中的每个元素依次(1到n)放进B中。B的第一个元素不大于A[i],把A[i]放在B的第一个位置,否则放在末尾。输出序列B。

思路:

双端队列+模拟,vector模拟会超时。

AC代码:

E2. Array Optimization by Deque(贪心+离散化+树状数组)

题意:

给一个长度为n的序列A,现有一个空的双端队列B,将A中的每个元素依次(1到n)放进B中。

即把A[i]放在B的第一个位置或者末尾,求逆序对最少的序列B的逆序对个数是多少。

逆序对:若i<j,则b[i]>b[j]。

思路:

局部最优能够保证全局最优,每次插入元素时在当前B基础上考虑插在前面还是后面,选择插入后逆序对少的位置。例:当前序列B【2,5,3,4】,此时的逆序对有【5,3】【5,4】;

如果要插入的元素是3,插入前面【3,2,5,3,4】此时的逆序对【3,2】【5,3】【5,4】;

将x插入后面时【2,5,3,4,3】此时的逆序对有【5,3】【5,4】【5,3】【4,3】。

数据进行离散化求逆序对只需要知道插入的元素比当前序列B中大的有几个,即只需要知道他们之间的相对关系,且a[i]的范围比较大,(1e-9~1e9)所以可以离散化。

数据离散化模板:

        for(int i=1;i<=n;i++)
        {
            cin>>a[i];//原数组
            b.push_back(a[i]);
        }
        sort(b.begin(),b.end());
        b.erase(unique(b.begin(),b.end()),b.end());//去重
        for(int i=1;i<=n;i++)
        {
            a[i]=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
        }//求出去重后从小到大的排名b[i]并赋值给a[i]

 树状数组动态维护(单点更新查询模板题)

当把a[i]放在前面时:sum(a[i]-1):查询排名小于a[i]-1的个数

当把a[i]放在后面时:sum(n)-sum(a[i]):大于a[i]的个数===总个数-小于a[i]的个数

       for(int i=1;i<=n;i++)
        {
           tmp=a[i];
           add(tmp,1);
           p1=sum(tmp-1);//将a[i]加在前面
           p2=sum(n)-sum(tmp);//将a[i]加在后面
           ans+=min(p1,p2);
        }
       

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=2e5+50;
ll n;
ll a[maxx];
ll tr[maxx];
ll lowbit(ll x)
{
    return x&-x;
}
void add(ll x,ll c)
{
    while(x<=n)
    {
        tr[x]+=c;
        x+=lowbit(x);
    }
}
ll sum(ll x)
{
    ll res=0 ;
    while(x>=1)
    {
        res+=tr[x];
        x-=lowbit(x);
    }
    return res;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n;
        vector<ll>b;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            b.push_back(a[i]);
        }
        sort(b.begin(),b.end());
        b.erase(unique(b.begin(),b.end()),b.end());
        for(int i=1;i<=n;i++)
        {
            a[i]=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
        }
        memset(tr,0,sizeof(tr));
        ll ans=0;
        ll tmp,p1,p2;
        for(int i=1;i<=n;i++)
        {
           tmp=a[i];
           add(tmp,1);
           p1=sum(tmp-1);//加在前面
           p2=sum(n)-sum(tmp);//加在后面
           ans+=min(p1,p2);
        }
        cout<<ans<<endl;
    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值