“浪潮杯”第九届山东省ACM大学生程序设计竞赛 - (A,C,E,F,G)

牛客网重现赛链接:https://www.nowcoder.com/acm/contest/123#question

A Anagram

题意:对于26个大写字母中的每一个字母operations一次就变为下一个字母,比如A操作一次变为B,B操作一次变为C,而Z操作一次变为A,给出等长的两个字符串str1,str2,问最少需要对第一个字符串str1操作几次使得其能够变为另一个字符串tmp,且转换后的字符串tmp中每个字符出现的次数与str2中每个字符出现的次数相同,这里允许字符排列不同只要个数对应即可。

解析:签到题之一,可直接用贪心来做,对于每个str2中的字符,只需往前找str1中最先出现的字母即可,比如str2中需要一个'C',那么看str1中有没有'C',有的话只需操作0次即可得到C,否则看str1中有没有'B',否则看str1中有没有'A',否则看str1中有没有'Z'...找到第一个出现的即可转换。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=27;
int mp[M][M];
int num1[M],num2[M];
string str1,str2;
void init()
{
    for(int i=0;i<26;i++)//mp[i][j]为i转换到j需要的操作次数
    {
        for(int j=0;j<26;j++)
        {
            mp[i][j]=(j-i+26)%26;
        }
    }
}
int main()
{
    init();
    while(cin>>str1>>str2)
    {
        memset(num1,0,sizeof(num1));
        memset(num2,0,sizeof(num2));
        int len1=str1.length();
        int len2=str2.length();
        for(int i=0;i<len1;i++)//记录字符出现情况
            num1[str1[i]-'A']++;
        for(int i=0;i<len2;i++)
            num2[str2[i]-'A']++;

        ll ans=0;
        for(int i=0;i<26;i++)
        {
            while(num2[i])
            {
                bool f=false;
                for(int j=i;j>=0;j--)
                {
                    if(num1[j])
                    {
                        f=true;
                        ans+=mp[j][i];
                        num2[i]--;
                        num1[j]--;
                        break;
                    }
                }
                if(f==false)
                {
                    for(int j=25;j>i;j--)
                    {
                        if(num1[j])
                        {
                            ans+=mp[j][i];
                            num2[i]--;
                            num1[j]--;
                            break;
                        }
                    }
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
C Cities

题意:有n个城市,每个有权值ai,现在想在城市间修一些路,使得所有城市链接起来,两个城市间修一条路的花费是两个城市权值之和,求最小花费。

解析:签到题,每个城市都连到权值最小的那一个城市即可

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=100005;
int n,a[M];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int minV=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=2;i<=n;i++)
        {
            if(a[i]<a[minV])
                minV=i;
        }
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            if(i!=minV)
            {
                ans+=(a[i]+a[minV]);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
F Four-tuples

题意:给出四个区间,让在每个区间中取一个数组成四元组(x1,x2,x3,x4),并且要求  ,问有能有多少种组合方式

解析:题意看似简单,但是要注意x1=x3,x2=x4是允许的,这一点是很可能想不到的,那么很容易想到用容斥来做,但注意这里要基于题干中四个不等于来做。具体见代码

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll l1,r1,l2,r2,l3,r3,l4,r4;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&l3,&r3,&l4,&r4);
        ll len1=(r1-l1+1);
        ll len2=(r2-l2+1);
        ll len3=(r3-l3+1);
        ll len4=(r4-l4+1);

        ll tl,tr,tl2,tr2;
        ll ans=len1*len2%mod;
        ans=ans*len3%mod;
        ans=ans*len4%mod;
        //此时ans为总可能方案数
        //下面进行容斥处理不合法的

        //减去满足一个不等式的
        
        //1与2交,即减去x1=x2的方案
        tl=max(l1,l2);
        tr=min(r1,r2);
        if(tl<=tr)
            ans=((ans-((tr-tl+1)*len3%mod*len4%mod))%mod+mod)%mod;

        //2与3交,即减去x2=x3的方案
        tl=max(l2,l3);
        tr=min(r2,r3);
        if(tl<=tr)
            ans=((ans-((tr-tl+1)*len1%mod*len4%mod))%mod+mod)%mod;

        //3与4交,即减去x3=x4的方案
        tl=max(l3,l4);
        tr=min(r3,r4);
        if(tl<=tr)
            ans=((ans-((tr-tl+1)*len1%mod*len2%mod))%mod+mod)%mod;

        //4与1交,即减去x4=x1的方案
        tl=max(l1,l4);
        tr=min(r1,r4);
        if(tl<=tr)
            ans=((ans-((tr-tl+1)*len2%mod*len3%mod))%mod+mod)%mod;

        //加上同时满足两个不等式的

        //1,2,3交,在计算1与2交和2与3交时,将1,2,3交的情况减去了两次,所以这里加回来
        //可以理解为,减去x1=x2和x2=x3时,将x1=x2=x3减了两次
        tl=max(l1,max(l2,l3));
        tr=min(r1,min(r2,r3));
        if(tl<=tr)
            ans=(ans+(tr-tl+1)*len4%mod)%mod;

        //1,2,4交
        tl=max(l1,max(l2,l4));
        tr=min(r1,min(r2,r4));
        if(tl<=tr)
            ans=(ans+(tr-tl+1)*len3%mod)%mod;

        //1,3,4交
        tl=max(l1,max(l3,l4));
        tr=min(r1,min(r3,r4));
        if(tl<=tr)
            ans=(ans+(tr-tl+1)*len2%mod)%mod;

        //2,3,4交
        tl=max(l2,max(l3,l4));
        tr=min(r2,min(r3,r4));
        if(tl<=tr)
            ans=(ans+(tr-tl+1)*len1%mod)%mod;

        //1,2交且3,4交
        tl=max(l1,l2);
        tr=min(r1,r2);
        tl2=max(l3,l4);
        tr2=min(r3,r4);
        if(tl<=tr&&tl2<=tr2)
            ans=(ans+(tr-tl+1)*(tr2-tl2+1)%mod)%mod;

        //1,4交且2,3交
        tl=max(l1,l4);
        tr=min(r1,r4);
        tl2=max(l3,l2);
        tr2=min(r3,r2);
        if(tl<=tr&&tl2<=tr2)
            ans=(ans+(tr-tl+1)*(tr2-tl2+1)%mod)%mod;

        //减去同时满足三个不等式的,即x1=x2=x3=x4
        
        //1,2,3,4交
        tl=max(l4,max(l2,max(l3,l1)));
        tr=min(r4,min(r2,min(r3,r1)));
        if(tl<=tr)
            ans=((ans-((tr-tl+1)*3%mod))%mod+mod)%mod;

        printf("%lld\n",ans);
    }
    return 0;
}
G Games

题意:Alice和Bob在玩游戏,有n堆石头,在每一回合中,玩家可以从堆中移除一些石块(数量必须是正数,并且不能大于堆中剩余石块的数量)。谁移除最后一块石头并且所有堆都空了,则他获胜,ALice是先手

但是在游戏开始之前,Bob可以选择一些石头堆并将其全部移除,可以去除的石头堆的数量是非负整数,并且不大于给定的数字d。注意d可 以大于n,在这种情况下,Bob可以去除所有的堆。

两者均选择最优策略,让求Bob有多少种取胜方式。

解析:这是用到NIm博弈原理的一个背包问题,NIm博弈问题的基本模型为:有三堆各若干个物品(个数分别为a,b,c),两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。这里的三个一般可推广至n个

nim博弈解决思路:先手必败态等价于a^b^c=0(^表示异或运算)。

那么我们只要为Bob设计一种去堆方式使得,Alice先手时有a1^a2^a3^...^an=0;

首先异或有如下运算性质,a^b=c等价于a^b^b=c^b即a=c^b;

那么我们要解的方程形式为:ai1^ai2^ai3^...^aim=0,这里aik是没被去掉的堆的石头数。

令dp[i][j][k]是前i堆去掉j堆的异或值为k的方案数,那么可得转移方程:

dp[i][j][k] = dp[i-1][j][k^a[i]] + dp[i-1][j-1][k];

意思为:前i堆去掉j堆的异或值为k的方案数dp[i][j][k]状态有两种来源

①.取第i堆石头作为第j堆被去掉的,则其方案数等于前i-1堆去掉j-1堆的异或值为k的方案数dp[i-1][j-1][k](去掉的不计异或和)

②.第i堆石头不去掉的,则其方案数等于前i-1堆去掉j堆的异或值为k^a[i]的方案数dp[i-1][j][k^a[i]](留下的计异或和)

可以实现用三维数组来存是因为d<=10,a[i]<=1e3,1e3的二进制为1111101000,他们之间异或不会超过1111111111即1023.

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int M=1e3+5;

int n,d,a[M];
ll dp[M][15][1024];//dp[i][j][k]是前i堆去掉j堆异或值为k的方案数

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&d);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);

        memset(dp,0,sizeof(dp));
        dp[1][0][a[1]]=1;//前1堆中一堆都不去
        dp[1][1][0]=1;    //前1堆中去掉一堆
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<=min(i,d);j++)
            {
                for(int k=0;k<=1023;k++)
                {
                    dp[i][j][k]=(dp[i-1][j-1][k]+dp[i-1][j][k^a[i]])%mod;
                }
            }
        }
        ll ans=0;
        for(int i=0;i<=min(n,d);i++)
            ans=(ans+dp[n][i][0])%mod;
        printf("%lld\n",ans%mod);
    }
    return 0;
}
E Sequence

题意:有序列(p1,p2,p3...pn),对于一个元素pi如果  存在j满足(1≤j<i) 且pj<pi .那么我们称pi为"good",现要从序列中去掉一个数,问去哪个数可以使得剩下的数中"good"最多。如果有多个输出值最小的。

解析:用数组cnt[i]表示数值i删除后减少的'good'的数量。

代码:

#include<bits/stdc++.h>
#define PI acos(-1.0)
#define ll long long
using namespace std;
#define inf 0x7fffffff
const ll M=1000010;

ll a,n;
ll cnt[M];

inline void Init()
{
    for(int i=1;i<=n;i++) cnt[i]=0;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld",&n);
        ll now_min=inf;
        ll pre_min=inf;
        Init();
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a);
            if(a<=now_min)
            {
                pre_min=now_min;
                now_min=a;
            }else if(a>=now_min&&a<pre_min)
            {
                cnt[now_min]++;
                cnt[a]++;
                pre_min=a;
            }
            else if(a>=pre_min)
            {
                cnt[a]++;
            }
        }
        ll val=cnt[1],it=1;
        for(int i=2;i<=n;i++)
        {
            if(cnt[i]<val) {val=cnt[i];it=i;}
        }
        printf("%lld\n",it);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sdau20163942/article/details/80377494