CF拉练第一场

来源:互联网 发布:淘宝重量运费模板 编辑:程序博客网 时间:2024/04/28 16:01

练习赛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 intusing 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 intusing 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 intusing 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 intusing namespace std;#define MAXN 50000+10char 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