2022牛客寒假算法基础集训营3

A:智乃的Hello XXXX

题目描述
智乃最近学习了C语言,在她学会编写 hello world 程序以后举一反三,还设计了一个hello XXXX的程序。
该程序可以通过“人工”智能自动识别,并向别人打招呼。
比如智乃在运行这段程序时就可以输出"hello chino",清楚运行的时候就会自动输出"hello qingchu"。

现在,开发这个“人工”智能程序的任务就交给你啦。

输入描述:
(本题无输入)

输出描述:
请向自己打招呼吧,输出一行一个字符串"hello XXXX",其中XXXX可以为任意字符串,包括空字符串。

示例1
输入
(None)

输出
hello chino

示例2
输入
(None )

输出
hello world

不能再水的一道题,只是让你输出hello…即可

各种答案都可以,主要是体现special judge

#pragma GCC optimize(2)
#include<bits/stdc++.h>
 
using namespace std;
//#define int long long
#define debug(a) cout<<#a<<" = "<<a<<endl;
typedef double db;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef long long ll;
const int inf = 0x3f3f3f3f;
 
 
//signed main()
int main()
{
    ios_base::sync_with_stdio(false), cin.tie(0);
    printf("hello Jacob\n");
     
    return 0;
}

B:智乃买瓜

题目描述
有一人前来买瓜。
“哥们儿,这瓜多少钱一斤呐”
“两块钱一斤”
“What’s up,这瓜皮是金子做的,还是瓜粒子是金子做的”
智乃来到水果摊前买瓜,水果摊上贩卖着N个不同的西瓜,第i个西瓜的重量为wi。智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为wi/2 。

也就是说对于每个西瓜,智乃都有三种不同的决策:
1.购买一整个重量为wi的西瓜
2.把瓜劈开,购买半个重量为wi/2的西瓜
3.不进行购买操作

为了简化题目,我们保证所有瓜的重量都是一个正偶数。

现在智乃想要知道,如果他想要购买西瓜的重量和分别为k=1,2,3…M时,有多少种购买西瓜的方案,因为这些数字可能会很大,请输出方案数对 10^9+7 取余数后的结果。

输入描述:
第一行输入两个整数N,M(0≤N≤10 ^ 3 ,1≤M≤10^3),分别表示西瓜的数目N,以及查询的重量上限为M。

若N不为0,接下来一行N个正偶数wi(2<=wi<=2*10^3)表示每个西瓜的重量。

输出描述:
输出一行M个数字,分别表示购买西瓜的重量和为k=1,2,3…M时,有多少种购买西瓜的方案,因为这些数字可能会很大,请输出方案数对 10^9+7 取余数后的结果。

示例1
输入
3 6
8 2 4

输出
1 2 1 3 2 3

说明
购买重量和为1的西瓜共1种方案:
①、2号西瓜劈开买半个。
购买重量和为2的西瓜共2种方案:
①、直接购买2号西瓜。
②、3号西瓜劈开买半个。
购买重量和为3的西瓜共1种方案:
①、2号西瓜劈开买半个再加上3号西瓜劈开买半个。
购买重量和为4的西瓜共3种方案:
①、1号西瓜劈开买半个。
②、直接购买2号西瓜,再加上3号西瓜劈开买半个。
③、直接购买3号西瓜。
购买重量和为5的西瓜共2种方案:
①、2号西瓜劈开买半个再加上直接购买3号西瓜。
②、2号西瓜劈开买半个再加上1号西瓜劈开买半个。
购买重量和为6的西瓜共3种方案:
①、直接购买2号西瓜,再加上直接购买3号西瓜。
②、1号西瓜劈开买半个,再加上直接购买2号西瓜。
③、1号西瓜劈开买半个,再加上3号西瓜劈开买半个。

示例2
输入
16 30
2 2 2 6 8 10 12 6 6 6 16 8 30 28 28 2

输出
4 10 20 37 65 109 174 269 401 580 817 1128 1525 2024 2640 3396 4304 5386 6660 8153 9880 11870 14138 16713 19605 22850 26462 30471 34881 39725

示例3
输入
3 5
100 102 104

输出
0 0 0 0 0

对于这个题我们可以想得简单一点,我们可以将这N个不同种类的西瓜,每个西瓜拆分成等质量的两份新的西瓜,这时每两份新的西瓜是属于同一种西瓜,因此我们很容易就想到,现在每种西瓜最多能选两个,这样一来,我们就可以套多重背包的模板求取方案的数量了。

用朴素多重背包就行,无需用二进制优化,算了一下时间复杂度为O(N * M),实际上是 O(2 * N * M),“2”表示西瓜数,可以忽略不计,算出来大概在2e6,在合法的范围内。

注意,多重背包中的体积对应题中输入的查询重量上限m价值对应的是西瓜的重量

以下是对西瓜进行转化之后的分析:

状态表示:

集合:dp[i][j] 所有只从前 i 种西瓜中选,且总重量恰好为 j 的方案的集合

属性:数量 count

状态计算:
和 多重 背包一致,根据最后一步选0, 1,2块瓜分类

不包含西瓜 i 的所有选法:
dp[i][j] = dp[i-1][j]

包含k个西瓜 i 的所有选法(k = 1 or 2)
dp[i][j] = dp[i-1][j-k*ai]

状态转移方程
总的方案数 = 不包含西瓜 i 的所有选法 + 包含k个西瓜 i 的所有选法

dp[i][j] = dp[i-1][j]+dp[i-1][j-k*ai]

状态初始化 and 答案

初始化:
dp[0][0]=1,初始 M 为 0 时,一个不选也是一种方案,其它均为 0(dp[0][1,2,3,…])

答案:dp[n][1,2,…,m]即为根据题目意思要求的答案

代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>

using namespace std;
//#define int long long
#define debug(a) cout<<#a<<" = "<<a<<endl;
typedef double db;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef long long ll;
const int inf = 0x3f3f3f3f;
//
int n,m;//西瓜的数目,以及查询的重量上限为
const int N = 1e3+10,M = 1e3+10,mod = 1e9+7;
int w[N];
int s[N];
int dp[N][N];

//signed main()
int main()
{
    ios_base::sync_with_stdio(false), cin.tie(0);

    cin>>n>>m;
    int W;
    for(int i=1;i<=n;++i)
    {
        cin>>W;
        s[i]=2 , w[i] = W/2;
    }

    dp[0][0] = 1;//这种情况
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<=m;++j)
        {
            for(int k=0;k*w[i]<=j&&k<=s[i];++k)
            {
                dp[i][j]=(dp[i][j]+dp[i-1][j-k*w[i]])%mod;
            }
        }
    }

    for(int i=1;i<m;++i) cout<<dp[n][i]<<' ';
    cout<<dp[n][m]<<endl;
    return 0;
}

D:智乃的01串打乱

题目描述
智乃有一个长度为N仅有字符’0’′和’1’ 组成的字符串,现在智乃想把这个01串打乱,变得和之前不一样。请你将这个01串打乱后输出。

打乱指的是,打乱后的01串和原本的01串中字符’0’和’1’ 的数目相同,字符串长度相同,且至少存在一个位置上的字符不同。

输入描述:
第一行输入一个正整数N(2≤N≤10^5)表示01串的长度。
接下来一行输入一个仅由字符’0’和’1’ 组成的字符串,保证其中字符0和1都至少出现一次。

输出描述:
仅一行,一个打乱后的01串,要求和原串至少有一个位置上的字符不同。

示例1
输入
2
01

输出
10

示例2
输入
10
1110101010

输出
0101010111

在字符串中,随便找到一对不相同的字符,将两者swap一下就行。

#pragma GCC optimize(2)
#include<bits/stdc++.h>

using namespace std;
//#define int long long
#define debug(a) cout<<#a<<" = "<<a<<endl;
typedef double db;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef long long ll;
const int inf = 0x3f3f3f3f;
//
const int  N = 1e5+10;
int n;
string s;

//signed main()
int main()
{
    ios_base::sync_with_stdio(false), cin.tie(0);
    cin>>n;
    cin>>s;
    for(int i=1;i<s.size();++i)
        if(s[i]!=s[i-1]) 
        {
            swap(s[i],s[i-1]);break;
        }
    cout<<s<<endl;
    return 0;
}

L:智乃的数据库

题目描述
智乃最近在学习数据库的查询语言SQL。SQL (Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统(RDBMS)。 SQL 的范围包括数据插入、查询、更新和删除,数据库模式创建和修改,以及数据访问控制。

使用SQL,可以灵活的对数据库表进行操作,在数据库的查询语句中有"GROUP BY"这样一种关键字。
GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组。

例如数据库中储存了这样一个数据库表Table,执行带有"GROUP BY"关键字的查询语句。
在这里插入图片描述
SELECT COUNT(*) FROM Table GROUP BY name;

SQL语句中COUNT表示聚合后每一个组中有多少条数据。
当执行上述的SQL查询语句后,其返回的结果如下

2
2 1

第一行的2表示按照name字段进行分组聚合,一共可以分出2组。
第二行的两个整数表示每组中各有多少条数据。

当然了,"GROUP BY"关键字是可以选中多个列进行分组聚合的,只需要把这些字段用逗号隔开即可。

SELECT COUNT(*) FROM Table GROUP BY name,val;

例如这样的SQL查询语句,执行后返回的结果如下

3
1 1 1

现在智乃把这张数据库表导出给你,请你执行一个SELECT COUNT(*) FROM Table GROUP BY …;的查询语句,请你把查询的结果告诉智乃。

为了简化问题,我们假设数据库的表由NN条记录以及MM个字段组成,且这些字段的类型均为int类型。

输入描述:

在这里插入图片描述
输出描述:

在这里插入图片描述
示例1
输入
4 3
id name val
1 1 2
1 1 3
1 2 1
2 2 2
SELECT COUNT(*) FROM Table GROUP BY name,id;

输出
3
1 2 1

说明
你可以按照自己喜欢的顺序输出
例如
3
2 1 1
则也能得到正确的结果。
主要是哈希表unordered_map,会用就行
注意:cin/scanf和getline的混用要注意的地方

#pragma GCC optimize(2)
#include<bits/stdc++.h>

using namespace std;
int n,m;
const int N = 1010;
string s[N];
string ss;
unordered_map<string,int> usi[N];

int main()
{
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;++i) cin>>s[i];
        for(int i=0;i<n;++i)
        {
                for(int j=0;j<m;++j)
                {
                        scanf("%d",&usi[i][s[j]]);
                }
        }
        
        getchar();//一定要注意把上方scanf产生的回车给吃掉下面才能getline
        getline(cin,ss);
        string sss = ss.substr(36);//下标为35的时候出现第一个逗号,因此我们要取其后面的子串才能分离出我们要分组的关键词
        vector<string> vs(N);
        int pos = 0;
        unordered_map<string,bool> usb;
        //这里一定要注意代码的书写以免debug
        for(int i=0;i<sss.size();++i)
        {
                if(sss[i]==','||sss[i]==';') {  usb[vs[pos]] = true,  pos++;  continue;}
                if(sss[i]!=','&&i!=sss.size()-1)
                        vs[pos]+=sss[i];//分离出关键词
        }
        
        unordered_map<string,int> usi2;
        set<string> ss;
        for(int i=0;i<n;++i)
        {
                string ssss;
                for(auto x : usi[i])
                {
                        string c = to_string(x.second);
                        if(usb[x.first]) ssss+=c,ssss+=",";//每加入一个数值产生的字符串注意要用","隔开,如果不加“,”会被hack
                }
                usi2[ssss]++;//每一组的数目加一
                ss.insert(ssss);//将当前组加入set
        }

        printf("%d\n",ss.size());//由于set有自动去重的功能,因此直接输出其大小就知道有多少个不同的组别
        for(auto x : usi2) printf("%d ",x.second);//输出每一组有多少个元素
        printf("\n");

        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值