CUGBACM Codeforces Tranning 1 解题报告

比赛链接:点击打开链接


自己太弱,只A了第一题。。。。。。

A题  水到没朋友。。。。

AC代码:

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<map>
#include<queue>
#include<set>
#define pi acos(-1.0)
#define eps 1e-8
typedef long long ll;
using namespace std;
int mi[50],mx[50],ans[50];
int d,sum;
int main()
{
    int i;
    scanf("%d%d",&d,&sum);
    int mi_sum=0,mx_sum=0;
    for(i=0; i<d; i++)
    {
        scanf("%d%d",&mi[i],&mx[i]);
        mi_sum+=mi[i],mx_sum+=mx[i];
    }
    if(sum<=mx_sum && sum>=mi_sum)
    {
        printf("YES\n");
        int rest = sum-mi_sum;
        for(i=0; i<d; i++) ans[i]=mi[i];
        for(i=0;i<d;i++)
        {
            if(rest ==0 ) break;
            int tmp=mx[i]-mi[i];
            if(rest>=tmp)
            {
                ans[i]=mx[i];
                rest-=tmp;
            }
            else if(rest< tmp)
            {
                //if(ans[i])
                ans[i]=mi[i] + rest;
                rest=0;
            }
        }

        for(i=0; i<d; i++)
        {
            printf("%d ",ans[i]);
        }
        printf("\n");

    }
    else printf("NO\n");
    return 0;
}

B题 dp

读题的时候竟然忽略了只能向下或者向右走这个关键一步!!!!!!所以一开始想成dfs了。。。然后正好看到学长们这道题尝试了几次T了,就更加坚信这一错误观点了。。。(他们T的原因是数组元素中有0的情况没有考虑到)。后来发现了,但是一方面时间不够,另一方面对路径的记录有些凌乱。所以。。。你懂的。

这题实际上就是一道棋盘dp。求路径上的数相乘乘积3结果末尾0的个数最少。因为零由2*5得到。所以对于棋盘上的每一个数,要记录它包含多少个2和5。

动态转移方程为:dp[i][j]=min(dp[i][j-1],dp[i-1][j])。result=min(dp[n-1][n-1][0],dp[n-1][n-1][1])。这道题的trick在于棋盘上的数有可能为0。如果不特判,必然T。

另一个需要强化的就是路径的记录。开一个二维数组记录路径,最后将 符合的路径存放在一个一维数组中输出。

AC代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<stack>
typedef long long ll;
#define pi acos(-1)
#define eps 1e-6;
using namespace std;
const int inf=1<<28;
const int maxn=105;
int mp[1050][1050],dp[1050][1050][2],has_zero,zero_x,zero_y;///dp[0]表示5,dp[1]表示2;
char dir[1050][1050][2];
char way[3050];
void get_cnt(int num,int i,int j)
{

    while(num % 5== 0 && num)
    {
        dp[i][j][0]++;
        num/=5;
    }
    while(num % 2 ==0 && num)
    {
        dp[i][j][1] ++ ;
        num/=2;
    }
}
int main()
{
    int n,i,j;
    memset(dp,0,sizeof(dp));
    has_zero=0;
    scanf("%d",&n);
    for(i=0; i<n; i++)
    {
        for(j=0; j<n; j++)
        {
            scanf("%d",&mp[i][j]);
            if(mp[i][j]== 0)
            {
                has_zero=1;
                zero_x=i,zero_y=j;
                dp[i][j][0] = dp[i][j][1] = 1;

            }
            else get_cnt(mp[i][j],i,j);

        }
    }
    for(i=0; i<n; i++)
    {
        for(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];
                    dir[i][j][k] = 'R';
                }
                else if (j == 0)
                {
                    dp[i][j][k] += dp[i-1][j][k];
                    dir[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];
                    dir[i][j][k] = dp[i-1][j][k] < dp[i][j-1][k] ? 'D' : 'R';
                }
            }
        }

    }
    if(min(dp[n-1][n-1][0],dp[n-1][n-1][1])>1 && has_zero)
    {
        printf("1\n");
        for (int i = 0; i < zero_x; i++) printf("D");
        for (int i = 0; i < n-1; i++) printf("R");
        for (int i = zero_x; i < n-1; i++) printf("D");
    }
    else
    {
        printf("%d\n",min(dp[n-1][n-1][0],dp[n-1][n-1][1]));
        int k=1;
        if(dp[n-1][n-1][0] < dp[n-1][n-1][1]) k=0;
        int cnt=0;
        int i=n-1,j=n-1;
        while(i!=0 || j!=0)
        {
            way[cnt++]= dir[i][j][k];
            if(dir[i][j][k]=='R') j--;
            else if(dir[i][j][k]=='D') i--;
        }
        for(i=cnt-1; i>=0; i--) putchar(way[i]);
    }
    puts("");
    return 0;
}


C题 一道模拟题 和五子棋差不多,变成了三子棋。难点在于要判断的情况太多了,其中,平局情况只有在两种棋子相加恰好等于9即无法再放棋子的时候,而不是无论如何放都不可能有一方赢的时候,这一点我考虑错了,所以一直在想怎么判断在棋子没填满的情况下判断是否是平局,显然是画蛇添足了。另外,我在做题的时候,判断太复杂,代码量很大,比赛结束后看了琦神代码才恍然大悟,我太弱了。

AC代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<stack>
typedef long long ll;
#define pi acos(-1)
#define eps 1e-6
using namespace std;
const int inf=1<<28;
const int maxn=105;
char mp[4][4];
int x_cnt,zero_cnt;
int win(int t)
{
    char c;
    int i,j;
    if(t==0) c='X';
    else c='0';
    int cnt=0;
    for(i=0; i<3; i++)
    {
        cnt=0;
        for(j=0; j<3; j++)
        {
            if(mp[i][j]==c) cnt++;
        }
        if(cnt==3) return 1;
    }
    cnt=0;
    for(j=0; j<3; j++)
    {
        cnt=0;
        for(i=0; i<3; i++)
        {
            if(mp[i][j]==c) cnt++;

        }
        if(cnt==3) return 1;
    }
    cnt=0;
    for(i=0; i<3; i++)
    {
        if(mp[i][i] == c) cnt++;
        if(cnt==3) return 1;
    }
    cnt=0;
    for(i=0; i<3; i++)
    {
        if(mp[i][2-i]==c) cnt++;
        if(cnt==3) return 1;
    }
    return 0;
}
int check()
{
    int a=win(0);
    int b=win(1);
//puts("xxxxx");
    if(a==1 && b==1) return 0;///0 illegal
    if(a==1 && x_cnt==zero_cnt)
    {
        return 0;
    }
    if(b==1 && x_cnt==zero_cnt+1) return 0;

    if(a==1) return 1;///first win;
    if(b==1) return 2; ///second win
    if(x_cnt+zero_cnt == 9) return 3; ///draw
    if(x_cnt==zero_cnt) return 4; ///first
    if(x_cnt==zero_cnt+1) return 5;///second
}
int main()
{
    int i,j;
    x_cnt=0,zero_cnt=0;
    for(i=0; i<3; i++)
    {
        scanf("%s",mp[i]);
    }
    for(i=0; i<3; i++)
    {
        for(j=0; j<3; j++)
        {
            if(mp[i][j] == 'X') x_cnt++;
            else if(mp[i][j] == '0') zero_cnt++;
        }
    }
    int ans= check();
    if((x_cnt>zero_cnt+1) || (x_cnt<zero_cnt)) printf("illegal\n");
    else if(ans==0) printf("illegal\n");
    else if(ans==1) printf("the first player won\n");
    else if(ans==2) printf("the second player won\n");
    else if(ans==3) printf("draw\n");
    else if(ans==4) printf("first\n");
    else if(ans==5) printf("second\n");
    return 0;
}

D题

给定正多边形三点,求符合条件的面积最小的正多边形面积。

一道几何题。因为时间不够了,比赛时没有看。通过给定三点求出三角形三边长,根据余弦定理,求得三个角大小,根据正弦定理a/sin(A)=2r进而求得外接圆半径。求三角形三角所对应的圆心角x,y,z。若正n边形满足条件,则设t=2*pi/n,则x y z都应是t的整数倍,并且 x/t + y/t + z/t == n 。其面积为0.5*r*r*sin(t)*n。

AC代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<stack>
typedef long long ll;
#define pi acos(-1.0)
#define eps 1e-6
using namespace std;
const int inf=1<<28;
const int maxn=105;
double x[5],y[5];
double edge[5];
double sita[5];
double r,per_sita;
void get_edge()
{
    edge[0]= sqrt((x[0]-x[1])*(x[0]-x[1]) + (y[0]-y[1]) * (y[0]-y[1]));
    edge[1]= sqrt((x[0]-x[2])*(x[0]-x[2]) + (y[0]-y[2]) * (y[0]-y[2]));
    edge[2]= sqrt((x[1]-x[2])*(x[1]-x[2]) + (y[1]-y[2]) * (y[1]-y[2]));
}

void get_sita()
{
    sita[0] = acos((edge[1]*edge[1]+edge[2]*edge[2]-edge[0]*edge[0])/(2*edge[1]*edge[2]));
    sita[1] = acos((edge[0]*edge[0]+edge[2]*edge[2]-edge[1]*edge[1])/(2*edge[0]*edge[2]));
    sita[2] = acos((edge[1]*edge[1]+edge[0]*edge[0]-edge[2]*edge[2])/(2*edge[1]*edge[0]));
   // cout<<sita[0]<<" "<<sita[1]<<" "<<sita[2]<<" "<<sita[0]+sita[1]+sita[2]<<"\n";

}
bool check(int n)
{
    int sum=0;
    for(int i=0; i<3; i++)
    {
        sum+=int((sita[i]+eps) / per_sita);
    }
    if(fabs(sum*per_sita-2*pi)<=eps) return true;
    return false;
}
int main()
{
    int i;
    for(i=0; i<3; i++)
    {
        scanf("%lf%lf",&x[i],&y[i]);
    }
    get_edge();
    get_sita();
    r=edge[0]/sin(sita[0])/2;///求外接圆半径
    for(i=0;i<3;i++) sita[i] *=2;
    for(i=3; i<=100; i++)
    {
        per_sita = 2*pi / i;
        if(check(i))
        {
            break;
        }

    }
    double s = 0.5*r*r *sin(per_sita)*i;
    printf("%.10lf\n",s);
    return 0;
}

E题

给一个字符串,只由“(”,“)”和“?”组成.。问号处可填“(”或“)”。并给定使用左右括号所需的cost。求cost最小的匹配括号串。对于当前串,只要左括号的个数大于右括号就可以继续。如果不符合,就要将之前?处改为左括号。根据贪心思想,使用的?必然是a-b最小的那个,所以可以使用一个优先队列来实现。串访问完后,只要判断左括号的数量是否为0即可。

AC代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<stack>
typedef long long ll;
#define pi acos(-1)
#define eps 1e-6
using namespace std;
const int inf=1<<28;
const int maxn=55005;

struct node
{
    int w,id;
    node (int x,int y) :w(x),id(y) {} ;
    bool friend operator <(node a,node b)
    {
        return a.w>b.w;
    }
};
priority_queue <node > q;
char s[maxn];
int main()
{
    scanf("%s",s);
    int n=strlen(s),a,b,i;
    int rest = 0;
    ll cost=0;
    for(i=0; i<n; i++)
    {
        if(s[i]=='(') rest++;
        else if(s[i]==')') rest--;
        else if(s[i]=='?')
        {
            s[i]=')';
            scanf("%d%d",&a,&b);
            cost+=b;
            q.push(node(a-b,i));
            rest--;

        }
        if(rest<0)
        {
            if(q.empty())
            {
                break;
            }
            else
            {
                node tmp=q.top();
                q.pop();
                s[tmp.id]='(';
                cost+=tmp.w;
                rest+=2;
            }
        }
    }
    if(rest) printf("-1\n");
    else  printf("%lld\n%s\n",cost,s);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值