CF拉练第一场

练习赛1

题目源地址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=61581#overview

  • Problem A Before an Exam (水题)
  • Problem B The least round way (DP)
  • Problem C Tic-tac-toe (模拟)
  • Problem D Ancient Berland Circus (几何)
  • Problem E Least Cost Bracket Sequence (贪心)
     比赛的时候制作出来A题,C题没做的出来主要是方法欠佳,考虑不周,其他的题所用到的知识方法还要多加练习,不然这次看懂了,下次还是没有印象,还是什么都写不出来。

Problem A Before an Exam

给定 d 天,每天可以学习 [mini,maxi] 个小时,父母要求的是sumtime个小时,上下限都要考虑下。 sumt 能否合理安排每天的学习,使得满足要求。

思路

求一下最小值和,最大值和,如果sum在之间,一定满足。

AC代码

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
    int d, sumtime, maxn, minn, x[100], y[100];
    cin >> d >> sumtime;

        maxn = 0;
        for(int i = 0; i < d; i++)
        {
            cin >> x[i] >> y[i];
            minn += x[i];
            maxn += y[i];
        }
        if(maxn < sumtime || minn > sumtime)
        {
            printf("NO\n");
            return 0;
        }

        int delta = maxn - sumtime;
        for(int i = 0; i < d; i++)
        {
            if(y[i] - x[i] >= delta)
            {
                y[i] -= delta;
                break;
            }
            else
            {
                delta -= y[i] - x[i];
                y[i] = x[i];
            }
        }
        printf("YES\n");
        for(int i = 0; i < d; i++)
            printf("%d ", y[i]);

    return 0;
}



Problem B The least round way

n×n 的矩阵,每个格子上有一个数字。现在从左上角走到右下角,每次只能向右、向下走,每走到一格,乘上该格子上的数字。问怎么走,能够使得这些数字的乘积末尾0最少。给出最少的0和路径。

思路

对于每一个数字,进行处理,我们只需要知道这个数字分别能被2、5整除多少次。
第一次走的时候,保证取2最小,(2一样的时候,5要最小),且在走的过程中记录路径。
然后再走一次,保证取5最小,(5一样的时候,2要最小),且在走的过程中记录路径。
最后的答案就是上面两个的最小值。
trick:有可能有数字为0,那乘积为0,答案为1,否则上述得到的最小值大于1,那么便不取,而是选择走0那条路径。

AC代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#include <functional>
#include <stdarg.h>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-4)
#define inf (1<<28)
#define ll long long int
using namespace std;

int mat[1005][1005];
char direct[1005][1005][2];                      //分别记录2,5整除次数最少的路径用D,R表示
int dp[1005][1005][2];                           //分别记录矩阵中每个数能被2,5整除的次数
char path[2010];
int path_len;

int factors(int num, int base)                     
{
    int ret = 0;
    int fct = base;
    if(num == 0)
        return 1;
    while(num % fct == 0)
    {
        ret++;
        fct *= base;                               //除数乘以base和被除数除以base一样
    }
    return ret;
}

int mi(int a, int b)
{
    return a < b ? a : b;
}

int main()
{
    int n;
    scanf("%d", &n);
    bool zero = false;                           //判断矩阵中是否有0
    int zero_i;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            scanf("%d", &mat[i][j]);
            if(mat[i][j] == 0)
            {
                zero = true;
                zero_i = i;                      //记录其中一个0所在的行
            }
            dp[i][j][0] = factors(mat[i][j], 2);
            dp[i][j][1] = factors(mat[i][j], 5);
        }
    }
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            for(int k = 0; k < 2; k++)                   //记录从起点到该点的最小次数,同时记录路径
            {
                if(i == 0 && j == 0) continue;
                if(i == 0)
                {
                    dp[i][j][k] += dp[i][j - 1][k];             
                    direct[i][j][k] = 'R';

                }
                else if(j == 0)
                {
                    dp[i][j][k] += dp[i - 1][j][k];
                    direct[i][j][k] = 'D';
                }
                else
                {
                    dp[i][j][k] += dp[i - 1][j][k] < dp[i][j - 1][k] ? dp[i - 1][j][k] : dp[i][j - 1][k];
                    direct[i][j][k] = dp[i - 1][j][k] < dp[i][j - 1][k] ? 'D' : 'R';
                }
            }
        }
    }
    if(mi(dp[n - 1][n - 1][0], dp[n - 1][n - 1][1]) > 1 && zero)
    {
        printf("1\n");
        for(int i = 0; i < zero_i; i++) printf("D");
        for(int i = 0; i < n - 1; i++) printf("R");
        for(int i = zero_i; i < n - 1; i++) printf("D");

    }
    else
    {
        printf("%d\n", mi(dp[n - 1][n - 1][0], dp[n - 1][n - 1][1]));
        path_len = 0;
        int k = 1;
        if(dp[n - 1][n - 1][0] < dp[n - 1][n - 1][1]) k = 0;

        for(int i = n - 1, j = n - 1; i != 0 || j != 0; )
        {
            path[path_len++] = direct[i][j][k];
            if(direct[i][j][k] == 'D')
            {
                i--;
            }
            else if(direct[i][j][k] == 'R')
            {
                j--;
            }
        }
        for(int i = 2 * (n - 1) - 1; i >= 0; i--)
            printf("%c", path[i]);
    }
    printf("\n");
    return 0;

}




Problem C Tic-tac-toe

3 X 3的棋盘,井字棋游戏,两个人连续在棋盘上放棋子(第一个人为X,第二个人为0),如果出现某行某列或者对角线上全为同样的棋子,那么该人获胜。
现在给出当下棋盘状态,让判断是那种状态

  • illegal 棋盘情况不合法
  • the first player won 第一个人赢
  • the second player won 第二个人赢
  • draw 平局
  • first 轮到第一个人走
  • second 轮到第二个人走 -

思路

主要illegal的情况比较多:

  • 双方棋子数不符合规则
  • 双方都赢了
  • 先手赢了,后手还放棋子
  • 后手赢了,先手还放棋子
还有就是要先判断是否合法,然后是否有赢家,然后是否平局,最后判断先后手

AC代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-4)
#define inf (1<<28)
#define ll long long int
using namespace std;

int xcnt, ocnt, num3;
char gao[3][3];

bool win(char tmp)                            //用函数表示会简单一些,我之前写的太挫了
{
    for(int i = 0; i < 3; i++)
    {
        if(gao[i][0] == tmp && gao[i][1] == tmp && gao[i][2] == tmp) return true;
        if(gao[0][i] == tmp && gao[1][i] == tmp && gao[2][i] == tmp) return true;
    }
    if(gao[1][1] != tmp) return false;
    if(gao[0][0] == tmp && gao[2][2] == tmp) return true;
    if(gao[2][0] == tmp && gao[0][2] == tmp) return true;
    return false;
}


bool legal()
{
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            xcnt += gao[i][j] == 'X';
            ocnt += gao[i][j] == '0';
        }
    }
    if(xcnt < ocnt || xcnt - ocnt > 1) return false;
    if(win('X') && xcnt == ocnt) return false;
    if(win'X' && win'0') return false;
    if(win('0') && xcnt != ocnt) return false;
    return true;
}

int main()
{
    xcnt = 0;
    ocnt = 0;
    for(int i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
            cin >> gao[i][j];
    if(!legal())                                      //判断非法
    {
        printf("illegal\n");
        return 0;
    }
    if(win('X'))                                     //判断赢家
    {

        printf("the first player won\n");
        return 0;
    }
    if(win('0'))
    {
        printf("the second player won\n");
        return 0;
    }
    if(xcnt + ocnt == 9)                             //判断平局
    {
        printf("draw\n");
        return 0;
    }
    if(xcnt == ocnt)                                //判断先后手
    {
        printf("first\n");
        return 0;
    }
    if(xcnt - ocnt == 1)
    {
        printf("second\n");
        return 0;
    }
}




Problem D Ancient Berland Circus

几何题,有一个正多边形(3<=n<=100),给出其中的三个顶点,问这个正多边形最小可能的面积。

思路

最小面积的话,就是要求正多边形的边数尽量少,给出三个点,可以求出外接圆



AC代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-4)
#define inf (1<<28)
#define ll long long int
using namespace std;

double a, b, c, r, q, s;
double A, B, C, angle, ans, n;
struct node
{
    double x, y;
}piller[3];

double dis(node a, node b)                   //计算两点间的距离
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

double fgcd(double a, double b)              //实型求余fmod函数,fgcd求出实数a,b的(近似)最大公约数
{
    if(fabs(b - 0) <= eps)
        return a;
    return fgcd(b, fmod(a, b));
}

void init()
{
    memset(piller, 0, sizeof(piller));
    for(int i = 0; i < 3; i++)
    {
        scanf("%lf%lf", &piller[i].x, &piller[i].y);
    }
    a = dis(piller[0], piller[1]);
    b = dis(piller[1], piller[2]);
    c = dis(piller[2], piller[0]);
    q = (a + b + c) / 2;                            //海伦公式
    s = sqrt(q * (q - a) * (q - b) * (q - c));
    r = a * b * c / (4 * s);
    A = acos(1 - a * a / r / r / 2);
    B = acos(1 - b * b / r / r / 2);
    C = 2 * pi - A - B;
}

int main()
{
    init();
    ans = pi / fgcd(A, fgcd(B, C)) * r * r * sin(fgcd(A, fgcd(B, C)));
    printf("%.8lf\n", ans);
    return 0;
}





Problem E Least Cost Bracket Sequence

有一串字符串,每个字符为’(‘, ‘)’ 或’?’。现在需要将其中的’?’替换为’(‘或’)’,使得最后所有括号能匹配。每个’?’替换成相应的括号都有一个花费。如果最后不能匹配,输出-1。否则输出最少的花费。

思路

优先队列的用法,cnt 记录了‘(’和‘)’的匹配状况,‘?’先当作‘)’处理,遇到‘(’cnt++,遇到')'cnt--;遇到‘?’时cnt--,同时输入两个取代值a,b,并将b - a,和当前位置配对存入优先队列 ,如果遇到cnt<0时,队列中踢出一组 即 之前当作‘)’处理的‘?’现在当作‘(’处理,cnt += 2,同时ss权值减去此处a, b 的差;如果此时没有备用的‘?’则不符合要求,输出-1;

AC代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-8)
#define inf 0x3f3f3f3f
#define ll long long int
using namespace std;
#define MAXN 50000+10

char s[MAXN];
ll ss;
int cnt;
int a, b;
priority_queue<pair<ll, int> > que;
int main()
{
    scanf("%s", s + 1);
    ss = 0;
    cnt = 0;
    for(int i = 1; s[i]; i++)
    {
        if(s[i] == '(')
        {
            cnt++;
        }
        else if(s[i] == ')')
        {
            cnt--;
        }
        else
        {
            scanf("%d%d", &a, &b);
            ss += b;
            cnt--;
            s[i] = ')';
            que.push(pair<ll, int> (b - a, i));
        }
        if(cnt < 0)
        {
            if(que.empty()) break;
            pair<ll, int> f = que.top();
            que.pop();
            ss -= f.first;
            cnt += 2;
            s[f.second] = '(';
        }
    }
    if(cnt != 0)
        printf("-1\n");
    else
        printf("%lld\n%s\n", ss, s + 1);
    return 0;
}









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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值