2013暑假集训B组训练赛第二场

本文精选了五道 CodeForces 上的经典算法题目,包括 Bus Game、Funky Numbers、Purification、Forming Teams 和 Queue at the School 的详细解答。通过分析问题背景、设计算法思路到具体实现步骤,帮助读者深入理解算法设计与应用。

Problem ACodeForces 79ABus Game 

这题先预处理求出三种拿钱方式(0、1、2张100),然后由这三种方式两两组合成9种方式;

然后按照优先选择的规则定义一个小于号,排一下序就ok了。

之后就是逐一的按照排好的顺序取钱。

#include <stdio.h>  
#include <algorithm>  
using namespace std;  
struct Meth{  
    int hud, ten;  
    void init(int hud, int ten){  
        this->hud = hud, this->ten = ten;  
    }  
    bool operator==(const class Meth &ans)const{  
        return hud==ans.hud&&ten==ans.ten;  
    }  
};  
//两种比较方式  
bool cmpC(const struct Meth &ans1, const struct Meth &ans2){  
    return ans1.hud>ans2.hud;  
}  
bool cmpH(const struct Meth &ans1, const struct Meth &ans2){  
    return ans1.ten>ans2.ten;  
}  
struct Comb{  
    Meth ci, ha;  
    int chud, cten;  
    void init(Meth a, Meth b){  
        ci.init(a.hud, a.ten), ha.init(b.hud, b.ten);  
        chud = ci.hud+ha.hud, cten = ci.ten+ha.ten;  
    }  
    bool operator<(const class Comb &ans)const{//按照规则定义小于号  
        return (cmpC(ci, ans.ci)||(ci==ans.ci&&cmpH(ha, ans.ha)));  
    }  
};  
Meth mat[3];  
Comb comb[9];//综合的使用方式  
int cnt, cnthud, cntten;  
void Prepare(){  
    for(int i = 0; i < 3; i++)  
        mat[i].init(i, 22-i*10);  
    cnt = 0;  
    for(int i = 0; i < 3; i++)  
        for(int j = 0; j < 3; j++)  
            comb[cnt++].init(mat[i], mat[j]);  
    sort(comb, comb+cnt);  
}  
int main(){  
    Prepare();  
    while(scanf("%d%d", &cnthud, &cntten) != EOF){  
        for(int i = 0; i < cnt; i++){  
            if(cnthud >= comb[i].chud && cntten >= comb[i].cten){  
                int a = cntten/comb[i].cten;  
                //可能会使用0张100(comb[i].chud=0)  
                if(comb[i].chud != 0)    a = min(a, cnthud/comb[i].chud);  
                cnthud -= a*comb[i].chud, cntten -= a*comb[i].cten;  
            }  
        }  
        if(cnthud*100+cntten*10>=220 && cntten >= 2){  
            printf("Ciel\n");  
        }else{  
            printf("Hanako\n");  
        }  
    }  
    return 0;  
}  

Problem BCodeForces 192AFunky Numbers 

水题,先求出所有的10^9以内的k(k+1)/2的数,排序。

然后每个询问,枚举第一个k(k+1)/2的数,然后二分查找是否存在匹配的一个数,也就是看预处理的数组里是否存在n-(k+(k+1)/2)这个数。

#include <stdio.h>  
#include <string.h>  
#include <algorithm>  
using namespace std;  
typedef long long LL;  
int cnt, n;  
LL num[45000];  
LL up = 1000000000;  
void prepare(){  
    cnt = 0;  
    for(LL i = 1; ;i++){  
        LL tmp = i*(i+1)/2;  
        if(tmp <= up)  num[cnt++] = tmp;  
        else break;  
    }  
}  
int main(){  
    prepare();  
    while(scanf("%d", &n) != EOF){  
        bool flag = false;  
        for(int i = 0; num[i] < n; i++){  
            int id = lower_bound(num, num+cnt, n-num[i])-num;//查找一个和num[i]互补的那种数  
            if(num[id]==n-num[i]){  
                flag = true;  
                break;  
            }  
        }  
        printf("%s\n", flag ? "YES" : "NO");  
    }  
    return 0;  
}



Problem CCodeForces 329APurification 
首先检查是否可行,不可行的条件就是同时有一整行都不能放并且有一整列不能放。(十字架的形状)

若可行,肯定是是下面两种情况:

1.每一行都有一列可以放,放在每一行的这个列

2.每一列都有一行可以放,放在每一列的这个行

#include <stdio.h>
#include <string.h>
const int maxn = 100+10;
char str[maxn];
int row[maxn], col[maxn], n, m;
int main(){
    while(scanf("%d%d", &n, &m) != EOF){
        for(int i = 1; i <= n; i++){
            memset(row, -1, sizeof(row));//该行全部都不能放
            memset(col, -1, sizeof(col));
        }
        for(int i = 1; i <= n ; i++){
            scanf("%s", &str[1]);
            for(int j = 1; str[j]; j++){
                if(str[j] == '.'){
                    row[i] = j, col[j] = i;//该行的j列放,该列的i行可放
                }
            }
        }
        int failr = -1, failc = -1;
        for(int i = 1; i <= n; i++){
            if(row[i]==-1)failr = i;
            if(col[i]==-1)failc = i;
        }
        if(failr!=-1&&failc!=-1){
            printf("-1\n");
            continue;
        }
        if(failr==-1){//每行都有一个列可放
            for(int i = 1; i <= n; i++)
                printf("%d %d\n", i, row[i]);
        }else{//每列都有个行可放
            for(int j = 1; j <= n; j++)
                printf("%d %d\n",  col[j], j);
        }
    }
    return 0;
}


Problem DCodeForces 216BForming Teams 
由于每个人最多只有两个矛盾的人,所有这些人的仇恨关系不是一条链就是一个环(这些链和环不会和其他的链或环有关)

于是如果我们把有矛盾的人都归为一个集合,关键就成了查找数量为奇数的环的个数了

利用并查集,只要两个人有矛盾关系就把他们归为一个集合。

当出现一个矛盾关系,如果这两个人之前就有矛盾则说明出现了环,判断若为奇数,肯定有一个人要坐板凳了。

#include <stdio.h>
#include <vector>
using namespace std;
const int maxn = 100+10;
int n, m, ans;
int fa[maxn], num[maxn];//集合的数量
int Root(int cur){
    if(fa[cur] != cur)
        fa[cur] = Root(fa[cur]);
    return fa[cur];
}
int main(){
    while(scanf("%d%d", &n, &m) != EOF){
        for(int i = 1; i <= n; i++)
            fa[i] = i, num[i] = 1;
        int a, b;
        ans = 0;
        for(int i = 1; i <= m; i++){
            scanf("%d%d", &a, &b);
            int r1 = Root(a), r2 = Root(b);
            if(r1 == r2 && (num[r1]%2==1))//ÕÒµ½Ææ»·
                ans++;
            else
                fa[r1] = r2, num[r2] += num[r1];
        }
        if((n-ans)%2==1)
            ans++;
        printf("%d\n", ans);
    }
    return 0;
}

Problem ECodeForces 266BQueue at the School 

这种题就是照着题目做,一步一步来就可以了
#include <stdio.h>
#include <string.h>
const int maxn = 60;
char str[2][maxn];
bool flag[maxn];
int n, t, now, pre;
int main(){
    while(scanf("%d%d", &n, &t) != EOF){
        now = 0, pre = 1;
        scanf("%s", str[now]);
        for(int i = 0; i < t; i++){
            now ^= 1, pre ^= 1;
            memset(flag, false, sizeof(flag));//要不要改
            for(int j = 0; j < n-1; j++){
                if(str[pre][j]=='B' && str[pre][j+1]=='G')//前面是男的后面是女的
                    flag[j] = flag[j+1] = true;//要改
            }
            for(int j = 0; j < n; j++)
                if(flag[j])
                    str[now][j]=(str[pre][j]=='G' ? 'B' : 'G');
                else
                    str[now][j]=str[pre][j];
        }
        str[now][n]=0;
        printf("%s\n", str[now]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值