Codeforces Beta Round #4 (Div. 2 Only)

题目链接

A. Watermelon
B. Before an Exam
C. Registration system
D. Mysterious Present

题目描述

A. Watermelon

给定一个数 w w w,能否将这个 w w w 分成两个偶数。可以就打印 YES,否则打印 NO

输入

8

输出

YES

数据范围
  • 1   ≤   w   ≤   100 1 ≤ w ≤ 100 1 w 100

分析: 只需要简单的分类讨论即可。

时间复杂度 O ( 1 ) O(1) O(1)

代码:

#include<iostream>
using namespace std;
 
int w;
int main(){
    scanf("%d",&w);
    
    if(w&1 || w==2){
        cout<<"NO"<<endl;
    }
    else cout<<"YES"<<endl;
    return 0;
}

B. Before an Exam

给定一个数 n n n 代表一共有 n n n 天,在给定一个 s u m sum sum,代表共有 s u m sum sum 个小时可以分配。每一天能够分配的小时数量,最小是 a i a_i ai,最大是 b i ( 1 < = i < = n ) b_i(1<=i<=n) bi(1<=i<=n)

问能否在这 n n n 天的每一天都分配一定的小时数, s u m sum sum小时必须用完

可以就打印 YES,并输出具体每一天分配的小时数;否则就打印 NO

输入

1 48
5 7

输出

NO

输入

2 5
0 1
3 5

输出

YES
1 4

数据范围
  • 1   ≤   n   ≤   30 1 ≤ n ≤ 30 1 n 30
  • 0   ≤   s u m ≤   240 0 ≤ sum≤ 240 0 sum 240
  • 0   ≤   a i ≤   b i   ≤   8 0 ≤ a_i≤ b_i ≤ 8 0 aibi 8

分析:
贪心的考虑:

  • 如果 sum < 最小的分配数 或者 sum > 最大分配数,都不能满足要求,直接打印NO
  • 否则,每一天都先分配最小的小时数,然后再遍历将剩余的小时数量用完即可。

时间复杂度 O ( n ) O(n) O(n)

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;


const int N = 40;
int a[N],b[N];
int f[N];


int main(){
    int n,sum;
    scanf("%d%d",&n,&sum);
    
    int mi = 0,ma = 0;
    for(int i = 1;i <= n;i++){
        scanf("%d%d",&a[i],&b[i]);
        mi += a[i],ma += b[i];
        f[i] = a[i];
    }
    
    //不可能成功分配的,直接打印 NO,结束程序
    if(sum < mi || sum > ma){
        puts("NO");
        return 0;
    }
    
    //每一天先分配最小的时间
    sum -= mi;
    
    
    for(int i = 1;i <= n && sum;i++){
        int d = b[i] - a[i];
        if(sum <= d){
            f[i] += sum;
            sum = 0;
        }
        else{
            f[i] += d;
            sum -= d;
        }
    }
    puts("YES");
    
    for(int i = 1;i <= n;i++) printf("%d ",f[i]);
    return 0;
}

C. Registration system

设计一个注册系统,给定你一些 字符串(用户名)进行注册。

如果该用户名 name 没有在系统中注册过,返回OK

否则就返回 “name1 name2 name3...”

输入

4
abacaba
acaba
abacaba
acab

输出

OK
OK
abacaba1
OK

输入

7
first
first
second
second
third
third
third

输出

OK
first1
OK
second1
OK
third1
third2

数据范围
  • 1   ≤   n   ≤   1 0 5 1 ≤ n ≤ 10^5 1 n 105

分析:
直接用一个哈希表记录相同字符串的次数,模拟即可。

时间复杂度 O ( n ) O(n) O(n)

代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
#include<unordered_map>
using namespace std;
 
const int N = 40;
 
 
int main(){
    int n;
    scanf("%d%d",&n);
    
    unordered_map<string,int> cnt;
    for(int i = 1;i <= n;i++){
        string s;
        getline(cin,s);
        if(!cnt.count(s)){
            cnt[s]++;
            puts("OK");
        }
        else{
            cnt[s]++;
            s += to_string(cnt[s] - 1);
            cout<<s<<endl;
        }
    }
    return 0;
}
}

D. Mysterious Present

给你一张卡片,这张卡片 长 h h h ,宽 w w w

在给你一些信封,这些信封 长 h i h_i hi,宽 w i w_i wi

大的信封才可以包小的,信封不能旋转,并且其中最小的信封也能包住卡片,卡片也不能旋转。

求最多能套多少张信封,以及具体是哪些信封(打印信封的序号,从1开始)

输入格式

信封的张数n 卡片宽度w 卡片长度h
第一张信封的宽度w1 第一张信封的长度h1
第二张信封的宽度w2 第二张信封的长度h2

输出格式

满足要求的最大信封张数
具体是哪些信封(按照宽,高,从小大大排序)

输入

2 1 1
2 2
2 2

输出

1
1

输入

3 3 3
5 4
12 11
9 8

输出

3
1 3 2

数据范围
  • 1    ≤   n   ≤   5000 1  ≤ n ≤ 5000 1  n 5000
  • 1   ≤   w ,    h    ≤   106 1 ≤ w,  h  ≤ 106 1 w,  h   106
  • 1   ≤   w i ,    h i   ≤   106 1 ≤ w_i,  h_i ≤ 106 1 wi,  hi 106

分析:
这道题就是 最长上升子序列模型的一个变形。

最长上升子序列:最长递增子序列

在输入数据的时候,我们只记录比卡片大的信封(比卡片长,比卡片宽)。

然后我们在对其进行排序(先按宽度排序,再按长度排序)。

我们定义 f ( i ) f(i) f(i) 是以第 i 个信封结尾的最大信封数量,再定义 p r e ( i ) pre(i) pre(i) 是从哪一个状态转移过来的(方便我们打印具体是哪些信封)。

在DP的过程中不断更新最大长度,然后我们再旋转其中的一条长度最大的信封序列打印输出。

时间复杂度 O ( n 2 ) O(n^2) O(n2)

代码:


#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
 
 
const int INF=0x3f3f3f3f,N=5005;
int f[N],pre[N];
 
struct node
{
    int w,h,id;
    bool operator<(const node &b)const
    {
        if(w==b.w)return h<b.h;
        return w<b.w;
    }
}a[N];
 
int main()
{   int n,w,h;
    int m = 0;
    
    scanf("%d%d%d",&n,&w,&h);
    for(int i=1;i<=n;i++)
    {
        int ww,hh;
        scanf("%d%d",&ww,&hh);
        //只记录大于 卡片 的信封信息
        if(ww>w&&hh>h)a[++m]=(node){ww,hh,i};
    }
    //m == 0 说明没有打于 卡片的信封,直接输出0
    if(m==0)printf("0\n");
    
    else
    {
        sort(a+1,a+m+1);
        f[0]=0;
        for(int i=1;i<=m;i++)
        {
            int pos=0;
            for(int j=1;j<i;j++)
                //后面的信封必须 长和宽 都比前面的信封大才行
                if(a[j].w<a[i].w&&a[j].h<a[i].h)
                    if(f[j]>f[pos])pos=j;
            f[i]=f[pos]+1;
            pre[i]=pos;
        }
        
        //找长度最大的信封序列
        int pos=1;
        
        for(int i=1;i<=m;i++)
            if(f[i]>f[pos])pos=i;
        printf("%d\n",f[pos]);
        
        //此时的pos是终点,需要根据 pre 去不断的找前面的点,直到找到起点为止
        vector<int>ans;
        while(pos)
        {
            ans.push_back(a[pos].id);
            pos=pre[pos];
        }
        for(int i=ans.size()-1;i>=0;i--)  printf("%d ",ans[i]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值