“卓见杯”第五届CCPC中国大学生程序设计竞赛河南省赛 - 部分题解

A: 最大下降矩阵 - LIS

时间限制: 1 Sec  内存限制: 512 MB
提交: 720  解决: 193
[提交] [状态] [讨论版] [命题人:外部导入]

题目描述

我们称一个矩阵是下降矩阵,当且仅当,矩阵的每一列都是严格下降的。很显然,这个要求很苛刻,大多数矩阵都无法满足。但是显然如果消去一些行,一定可以使得这个矩阵变成下降矩阵。

现在给出一个n行m列的矩阵,请你求出最少消去多少行,可以使得这个矩阵变为下降矩阵。

输入

输入第一行包含两个正整数n,m分别表示矩阵的行数和列数。(1<=n,m<=300)
接下来n行,每行有m个数,中间用空格隔开,每个数都小于2^31.

输出

输出仅包含一个整数,即最少消去的行数。

样例输入 Copy

1 3
1 2 3 

样例输出 Copy

0

提示

样例二
输入
3 1
3
1
2
输出
1

思路:

把矩阵的每一行看成是一个数,对列求最长下降子序

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=315;
int a[N][N],dp[N];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    int mx=-INF;
    for(int i=1;i<=n;i++){
        dp[i]=1;
        for(int j=i-1;j>=1;j--){
            int flag=0;
            for(int k=1;k<=m;k++){
                if(a[j][k]<=a[i][k])flag=1;
            }
            //printf("flag=%d\n",flag);
            if(!flag){
                //printf("%d %d\n",dp[j]+1,dp[i]);
                dp[i]=max(dp[j]+1,dp[i]);
            }
        }
        mx=max(dp[i],mx);
    }
    printf("%d\n",n-mx);
}

D: 文本修正 - 模拟

时间限制: 1 Sec  内存限制: 256 MB
提交: 698  解决: 295
[提交] [状态] [讨论版] [命题人:外部导入]

题目描述

Chika接到了去检查河南省算法竞赛题面的任务,她发现所有单词"Henan"的首字母都没有大写。她需要去修正文本中的所有错误。换句话说,她需要把所有单词"henan"的首字母从"h"替换为"H",同时保留文本的其余部分不变。你能帮助她吗?

输入

输入文件仅包含一行,包含被空格分割开的单词,代表Chika被要求去检查的文本。输入的字符总数不超过200,只含有大小写字母和空格。要注意,只有被空格分割开的仅包含字母的连续串才被称为“单词”。

输出

输出文件应该只包含一行,代表你修正后的文本。

样例输入 Copy

Hello henan algorithm contest

样例输出 Copy

Hello Henan algorithm contest

思路:

模拟一下就好了,注意是“被空格分割的”字符

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=115;
 
int main(){
    string s;
    getline(cin,s);
    int len=s.length();
    if(len>5&&s[0]=='h'&&s[1]=='e'&&s[2]=='n'&&s[3]=='a'&&s[4]=='n'&&s[5]==' '){
        s[0]='H';
    }
    else if(len==5&&s[0]=='h'&&s[1]=='e'&&s[2]=='n'&&s[3]=='a'&&s[4]=='n'){
        s[0]='H';
    }
    if(len>5&&s[len-6]==' '&&s[len-5]=='h'&&s[len-4]=='e'&&s[len-3]=='n'&&s[len-2]=='a'&&s[len-1]=='n'){
        s[len-5]='H';
    }
    for(int i=1;i<len;i++){
        if(i+5<len&&s[i-1]==' '&&s[i]=='h'&&s[i+1]=='e'&&s[i+2]=='n'&&s[i+3]=='a'&&s[i+4]=='n'&&s[i+5]==' '){
            s[i]='H';
        }
    }
    cout<<s<<endl;
}

E: 咕咕的的复复读读机机 - 模拟

时间限制: 1 Sec  内存限制: 64 MB
提交: 508  解决: 290
[提交] [状态] [讨论版] [命题人:外部导入]

题目描述

咕咕一直想买台复读机,今天他终于走进了一家卖复读机的小店!这里有很多很多的复读机,咕咕看中 了一台相貌平平无奇的,他决定试用一下这台复读机的功能。然而,当他打开复读机的开关后,复读机 说了 n 个数字,而且这些数字不完全一样——作为一台复读机,怎么会这样呢?看到满脸疑惑的咕咕, 店员说道:” 这是一台带有自动加噪的复读机,它虽然 n 次中说的数字不完全一样,但是它所复读的 那个数字永远会在这 n 次中出现次数最多!” 
于是,聪明的你,知道这 n 个数字之后,你能猜出那个数字是它所复读的数字吗? 
于是,聪明的你,知道这 n 个数字之后,你能猜出那个数字是它所复读的数字吗? 

输入

第一行一个数字 n(1 ≤ n ≤ 100)。 
第二行有 n 个数字(范围在 [1,100] 之间),表示这台复读机所说的 n 个数字。
 这台复读机保证它所复读的那个数字是唯一的! 

输出

输出那个数字。 

样例输入 Copy

3
2 4 2

样例输出 Copy

2

提示

样例二
输入
5
1 1 100 99 87
输出
1
思路:

就判断谁出现最多就好了

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=115;
int a[N],cnt[N];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        cnt[a[i]]++;
    }
    int mx=0,ans;
    for(int i=1;i<=n;i++){
        if(mx<cnt[a[i]]){
            ans=a[i];
            mx=cnt[a[i]];
        }
    }
    printf("%d\n",ans);
}

F: 咕咕的计数题 II

时间限制: 1 Sec  内存限制: 64 MB
提交: 1432  解决: 143
[提交] [状态] [讨论版] [命题人:外部导入]

题目描述

咕咕最近在学习初等数论,并且对下取整函数产生了极大的兴趣。下取整函数是指一个函数,自变量为 一个实数,因变量为一个整数,这个整数恰好是小于或等于自变量的最大的整数,通常记做 ⌊x⌋。例如, ⌊2.5⌋ = 2,⌊2⌋ = 2,⌊−2.5⌋ = −3。
 咕咕发现,给定一个 a,并不是所有的自然数 n 都存在一个正整数 i 使得 ⌊n/i⌋ = a。那么,如果给定 l,r,咕咕好奇在区间 [l,r] 中有多少个正整数能使这个等式有正整数解 i 呢?
 那么,聪明的你,你能告诉咕咕吗? 

输入

第一行有一个整数 T(1 ≤ T ≤ 106),表示数据组数。接下来有 T 行,每行有三个数 a,l,r(1 ≤ a ≤ 1018,1 ≤ l ≤ r ≤ 1018),表示一组询问。 

输出

输出 T 行,对每组询问,输出一个整数表示答案。 

样例输入 Copy

4
5 7 10
7 39 42
1000 1000 1000
27 100 1000

样例输出 Copy

1
2
1
617

思路:

可以看出来规律:对于数x,以下满足:

x*1

x*2  x*2+1

x*3  x*3+1  x*3+2

x*4  x*4+1  x*4+2  x*4+3

……

x*x以及以后

写的时候注意特判情况:

3 6 6

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=115;
 
int main(){
    int t;
    ll a,l,r;
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld%lld",&a,&l,&r);
        ll ans=0,e=0,s,t;
        int flag=0;
        if(a<=(int)sqrt(l)+10){
            if(a*a<=l){
                printf("%lld\n",r-l+1);
                continue;
            }
        }
        if(a<=(int)sqrt(r)+10){
            if(a*a<=r){
                ans+=r-a*a+1;
                e=a-1;
                flag=1;
            }
        }
        s=l/a;
        if(!flag){
            if(s*a<=l&&s*a+s-1>=r){
                printf("%lld\n",r-l+1);
                continue;
            }
            e=r/a;
            t=a*e+e-1;
            if(r<=t){
                ans+=r-e*a+1;
                e-=1;
            }
        }
        t=s*a+s-1;
        if(l<=t){
            ans+=s;
            ans-=(l-s*a);
        }
        s+=1;
        if(s<=e)ans+=(s+e)*(e-s+1)/2;
        printf("%lld\n",ans);
    }
}

H: 咕咕的搜索序列 - LCA

时间限制: 1 Sec  内存限制: 128 MB
提交: 364  解决: 39
[提交] [状态] [讨论版] [命题人:外部导入]

题目描述

    咕咕已经学到树上的深度优先搜索 (dfs) 啦!由于同一棵树不同的 dfs 访问结点的次序不一样,咕咕干脆定义 了一个搜索序列:一开始序列为空,而每次离开这个点,并且不会再返回这个点时,就把这个点加入序列中, 最后返回到根节点后也把根节点加入这个序列中,这样就定义了一个与 dfs 一一对应的搜索序列!而且这个 搜索序列,也是所有点的一个排列。 
    对于一棵有根树(结点标号 1 到 n,以 1 为根),咕咕对它跑了一遍 dfs 得到了搜索序列后,它准备把这个序 列抄在纸上拿给咸鱼看。但是,粗心的咕咕在抄这个序列的时候,一些点被它忽略了,纸上的序列只有 m 个 点。待咸鱼看到纸上这个序列后,咸鱼就很好奇:咕咕那么粗心,只是抄少了点这么简单吗,会不会同时把 一些点的位置也给变化了呢?
     现在,聪明的你,你能判断出来咕咕在抄的时候有没有把点的位置变化了吗?也就是说,咕咕给的 m 个点的 序列,真的能够由一个 dfs 得到的搜索序列删除几个点后得到吗? 

输入

第一行一个整数 T(1≤ T ≤106),表示有 T 组数据。 
对于每组数据:
 第一行有两个正整数 n 和 m(1≤m≤n≤106),表示树的点数和咕咕给的序列的点数。
 第二行有 n−1 个正整数 a1,a2,··· ,an−1(1≤ ai ≤i),表示点 ai 是点 (i+1) 的父结点。 
第三行有 m 个互不相同的正整数,b1,b2,··· ,bm(1≤bi ≤n),表示咕咕给的序列。 
输入保证同一个测试点下的所有数据的 n 的和不超过 106。 

输出

对每一组数据,输出一行。如果一定不能得到,输出 BAD GUGU ;否则输出 NOT BAD 。

 

样例输入 Copy

2
4 4
1 1 2
3 4 2 1
4 2
1 1
2 2 4

样例输出 Copy

NOT BAD
BAD GUGU

提示

对于样例中给定的树,只存在两个不同的所搜序列:(3,4,2,1) 和 (4,2,3,1)。故对于序列 (2,4) 是没有办法 由任意一个搜索序列只通过删除点得到的。

思路:

搜到一个点时,从这个点向下搜,这些点肯定在这个点之前都搜了,标记一下已访问。

然后每两个相邻的点i,i+1,找LCA,设LCA是点A

找到点i到点A的路上,最接近点A的点B,即点A的到i的下一层,若从i到i+1 ,dfs则,B以下的点肯定都搜过了标记一下

(dfs的过程是i先到B,再往i+1点的方向走,最后搜点A)

此时,若i+1已经访问过了,则输出“BAD GUGU”

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=1000015;
int cnt,head[N];
struct A{
    int to,nex;
}edge[N];
int q[N],vis[N],pre[N],dep[N];
  
void add(int from,int to){
    edge[cnt].to=to;
    edge[cnt].nex=head[from];
    head[from]=cnt++;
}
  
void dfs(int now){
    if(vis[now])return ;
    vis[now]=1;
    for(int i=head[now];i!=-1;i=edge[i].nex){
        int v=edge[i].to;
        dfs(v);
    }
}
void dfs_dep(int now,int last){
    dep[now]=dep[last]+1;
    for(int i=head[now];i!=-1;i=edge[i].nex){
        int v=edge[i].to;
        dfs_dep(v,now);
    }
}
  
int lca(int u,int v){
    int lastu=u;
    while(dep[u]>dep[v]){
        lastu=u;
        u=pre[u];
    }
    while(dep[v]>dep[u]){
        v=pre[v];
    }
    if(u==v)return lastu;
    while(pre[u]!=pre[v]){
        u=pre[u];
        v=pre[v];
    }
    return u;
}
  
int main(){
    int t,n,m,a;
    scanf("%d",&t);
    while(t--){
        int flag=0;
        cnt=0;
        scanf("%d%d",&n,&m);
        //memset(edge,0,sizeof(edge));
        for(int i=0;i<=n;i++)head[i]=-1;
        for(int i=2;i<=n;i++){
            scanf("%d",&a);
            pre[i]=a;
            add(a,i);
            vis[i]=0;
        }
        vis[1]=0;
        dfs_dep(1,0);
        for(int i=1;i<=m;i++){
            scanf("%d",&q[i]);
        }
        for(int i=1;i<m;i++){
            dfs(q[i]);//把当前点的子树都标记为1
            dfs(lca(q[i],q[i+1]));//找到B点
            if(vis[q[i+1]]){
                flag=1;
                break;
            }
        }
        if(flag)printf("BAD GUGU\n");
        else printf("NOT BAD\n");
    }
}

I: Childhood dream - 暴力+剪枝

时间限制: 1 Sec  内存限制: 256 MB
提交: 300  解决: 95
[提交] [状态] [讨论版] [命题人:外部导入]

题目描述

你童年时期就有一个梦想,想要加入 ACM(Association of Calculation and Magic),今天,这个机会终于 来了。 
但是 ACM 只想要哪些天赋异禀的人, 比如像 tourist,他们给了你一道题来检测你是否足够机智。 
猜一个长度为 m 数字串,总共有 n 个提示串,解释如下:
 8640 0A2B 
A 前面的数字说明与答案相比,有多少个位置上的数字是相同的。 B 前面的数字说明与答案相比,有多 少个数字是相同的,但是位置不一样。
 0 A 就表示给出的串没有任何位置和答案是相同的。 2 B 就表示给出的串中有两个数字和答案相同,但 是位置不一样。 
所以,对于上面那个提示串 6457 是一个合理的答案,但是 1234 并不是。 
现在给你 N(N<=100) 个提示串(如上所示),你需要去找到一个数字串来符合每一个提示串的要求。
提示串中的每个数字都是不同的,即一个串中不会存在相同的数字。 
你能解决这个问题并加入 ACM 吗? 

输入

第一行两个数字,n(n<=100) 和 m(m<=9), 提示串的数量以及目标字符串的长度。 
然后是 n 行,每行的格式如下: 
s x y 
s 是提示串,x 是 A 前的数字,y 是 B 前的数字,等同于: 
s xAyB 

输出

一行,目标串。
数据保证答案唯一。 

样例输入 Copy

6 4
5164 3 0
5174 3 0
5194 3 0
5124 3 0
5134 3 0
5104 3 0

样例输出 Copy

5184

思路:

暴力即可,妹想到啊

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=115;
int flag,n,m,a[N][N],p[N],x[N],y[N];
 
int judge(int m1){
    for(int i=1;i<=n;i++){
        int cnt1=0,cnt2=0;
        for(int j=1;j<=m1;j++){
            if(p[j]==a[i][j])cnt1++;
        }
        if(m1<m&&cnt1>x[i])return 0;
        if(m1==m&&cnt1!=x[i])return 0;
        for(int j=1;j<=m1;j++){
            for(int k=1;k<=m1;k++){
                if(j==k)continue;
                if(a[i][j]==p[k]){
                    cnt2++;
                    break;
                }
            }
        }
        if(m1<m&&cnt2>y[i])return 0;
        if(m1==m&&cnt2!=y[i])return 0;
    }
    return 1;
}
 
void dfs(int dep){
    if(flag)return ;
    if(dep==m+1){
        flag=1;
        for(int i=1;i<=m;i++){
            printf("%d",p[i]);
        }
        printf("\n");
    }
    if(dep>m+1)return ;
    for(int j=0;j<=9;j++){
        p[dep]=j;
        if(judge(dep))dfs(dep+1);
        else continue;
    }
}
 
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%1d",&a[i][j]);
        }
        scanf("%d%d",&x[i],&y[i]);
    }
    dfs(1);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值