第三届中国计量大学ACM程序设计竞赛个人赛(同步赛)(A 表达式求值 B 思维 D 模拟 H 模拟 L 线段树 M 博弈论 N 汉诺塔)

打的好菜

题目链接

A-Arithmetic Problems

题意:实现 计算表达式,注意这里的加减乘除换了个意思如下:

- 代表是乘 

/代表是加

+代表是除

x代表是减。

两个单调栈就可以解决了。

由于这里没有括号,所以我用了另一种方法求表达式求值,双端队列

遇到乘、除就计算,遇到+ -就放着。最后从前面往后扫就可以了。

但是呢,这种方法不适用于有括号的情况,赛后百度有没有栈的做法,发现都好乱好乱,又复杂又乱,这么简单的东西还搞什么优先级出来,不愧是专家级别想出的算法,很严谨,但也很难写,不适合在竞赛中写。

然后分析我的方法是否可以改善,答案是可以的。我就判断  字符队列最后一个是不是‘-’ 符号,是的话就将当前的数变成负数,然后字符队列删掉'-',放一个'+' 就可以了。在num放入数字队列的时候加一个if就可以了。

括号怎么处理?遇到右括号,就疯狂的计算栈顶的数 直到遇到左括号就行了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
deque<double>num;
deque<char>op;
const double esp=1e-11;
string s;

int cal(char c){if('0'<=c&&c<='9') return 1;else return 0;}

int main()
{
    getline(cin,s);

    for(int i=0;i<s.size();++i){
        if(s[i]==' ') continue;

        if(cal(s[i])){
            double now=0;
            int j=i;
            for(;j<s.size();++j){
                if(s[j]==' ') continue;
                if(cal(s[j])) now=now*10+s[j]-'0';
                else break;
            }
            //printf("now:%f\n",now);


            if(op.size()&&(op.back()=='*'||op.back()=='/')){

                if(op.back()=='/'&&now==0){puts("Cannot be divided by 0");return 0;}

                if(op.back()=='*') now=num.back()*now;
                else now=num.back()/now;
                num.pop_back();op.pop_back();
            }

            num.push_back(now);
            i=j-1;
        }
        else op.push_back(s[i]);

    }

    double ans=num.front();num.pop_front();

    while(op.size()){
        if(op.front()=='+') ans=ans+num.front();
        else ans=ans-num.front();

        num.pop_front();op.pop_front();
    }
    printf("%.2f\n",ans);
}
/*
1-2-3-4

*/

B-Birthday Gift

题意:给你n组(xi,yi),现在你选两组出来,使得min(xi+yj,yi+xj) 最大。

做法:假设xi+yj<yi+xj   => xi-yi<xj-yj,那么我们对xi-yi 从大到小排序后,从大到小枚举。

枚举到当前的(xi,yi) 由于排序后,所有的j<i  都满足:xi+yj<yi+xj  xi 已知  yi 已知,那我们只需要最大的yj 就可以了,不用管xj

假设 xi+yj>yi+xj 其实后面的跟上面是一样的操作,只是下标变化了而已。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=2e5+10;
struct node
{
    ll u,v,w;
}a[N];
int n;
bool cmp(node a,node b)
{
    return a.w>b.w;
}
int main()
{
    n=read();
    rep(i,1,n) a[i].u=read();
    rep(i,1,n) {
        a[i].v=read();
        a[i].w=a[i].u-a[i].v;

    }
    sort(a+1,a+1+n,cmp);

    int id=1;
    ll mx=a[1].v;
    ll ans=0;
    for(int i=2;i<=n;++i){
        ans=max(ans,min(a[i].u+a[id].v,a[id].u+a[i].v));
        if(a[i].v>mx){
            mx=a[i].v;
            id=i;
        }
    }
    printf("%d\n",ans);


}

D-Stay up Late and Wake up Early

题意:从某个幸运的时刻开始 每隔x分钟醒来一次,最后到h:m 恰好醒来。x、h、m已知,求最小的醒了次数,

幸运时刻就是 小时 或 分钟 中带有7的。

做法:简单的从h:m 往前递推模拟就可以呃了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e2+10;
char s[N];
int cal(int n,int m)
{
    if(n%10==7) return 1;
    if(n/10%10==7) return 1;
    if(m%10==7) return 1;
    if(m/10%10==7) return 1;
    return 0;
}
int main()
{
    int _=read();while(_--)
    {
        int x=read();
        scanf("%s",s+1);
        int h=(s[1]-'0')*10+s[2]-'0';
        int m=(s[4]-'0')*10+s[5]-'0';
        //printf("hh:%d m:%d\n",h,m);

        int ans=0;
        while(1)
        {
            if(cal(h,m)){
                printf("%d %02d:%02d\n",ans,h,m);
                break;
            }
            if(m>=x) m-=x;
            else{
                m+=60;
                m-=x;
                h--;
                if(h<0) h=23;
            }
            //printf("h:%d m:%d\n",h,m);
            ans++;
        }


    }
}

H-Words Interesting

题意:给你文本串s,n个子序列串t,

1、s每个字符至少使用一次,

2、t串中不能没有s串没有的,

3、t串中的顺序 是在s串的顺序

做法:大大大水题,看错题了,以为s串每个字符只能被用一次。

n又只有20  这不就是简单的水题了嘛,代码都不想写了,直接贴别人的代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+10,MM=2e7+10,mod=1e9+7;
int n,m;
char s[N],p[N];
int c[26],cc[26];
void solve(){
    memset(c,0,sizeof (c));
    memset(cc,0,sizeof (cc));

    int x;
    scanf("%s%d",s+1,&x);
    int flag=1;
    n=strlen(s+1);
    for(int i=1;i<=n;i++)c[s[i]-'a']++;
    for(int i=1;i<=x;i++){
        scanf("%s",p+1);
        int m=strlen(p+1);
        int now=1;
        for(int i=1;i<=m;i++)cc[p[i]-'a']++;
        for(int i=1;i<=n;i++){
            if(s[i]==p[now])now++;
        }
        if(now<=m) flag=0;

    }
    if(flag==0){
         printf("No\n");return;
    }
    for(int i=0;i<26;i++){
        if(c[i]>cc[i]){
            printf("No\n");return;
        }
        if(c[i]==0&&cc[i]){
            printf("No\n");return;
        }
    }
    printf("Yes\n");
}
int main()
{
    int _;cin>>_;while(_--)solve();
    return 0;
}
/*
20

abecd 2
aec
bed

*/

L-Yet Another Bracket Sequence

题意:给你一堆左括号和右括号,每次修改一个位置 若是左括号修改为右括号,若是右括号,修改为左括号,问每次修改后是否是平衡的括号序列。

做法:cf原题,线段树维护动态括号序列。原题链接:链接

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
/*
sum 维护的是左右括号的个数 为零的时候说明 左右括号数量相等
mi值维护的是最小值,最小值越小,那么连续的未匹配的右括号越多))))当mi[1]!=0 那么就是不匹配的
因为又剩余的))
mx值维护的是最大值,最大值越大,那么连续的未匹配的左括号越多((((,
当整个括号序列是匹配的,那么mx[1]就是维护的答案( 嵌套的最大括号)
*/

int sum[maxn<<2],mi[maxn<<2],ma[maxn<<2],n,m;
char s[maxn];

void update(int o,int l,int r,int pos,int c)
{
    if(l==r)
    {
        sum[o]=c;mi[o]=c;ma[o]=c;
        return ;
    }
    int m=(l+r)>>1;
    if(pos<=m) update(o<<1,l,m,pos,c);
    else update(o<<1|1,m+1,r,pos,c);
    sum[o]=sum[o<<1]+sum[o<<1|1];
    //很妙的更新方程:类似于动态更新前缀和
    //我一直以为认为前缀和无法用线段树动态更新,现在这个方程证明了是可以的
    ma[o]=max(ma[o<<1],sum[o<<1]+ma[o<<1|1]);
    mi[o]=min(mi[o<<1],sum[o<<1]+mi[o<<1|1]);
}

int main()
{

    scanf("%d%d",&n,&m);
    scanf("%s",s+1);

    for(int i=1;i<=n;++i) {
        if(s[i]=='(') update(1,1,n,i,1);
        else update(1,1,n,i,-1);
    }
    while(m--)
    {
        int x;scanf("%d",&x);
        if(s[x]=='('){
            update(1,1,n,x,-1);
            s[x]=')';
        }
        else{
            update(1,1,n,x,1);
            s[x]='(';
        }

        if (mi[1]!=0 ||sum[1]!=0) puts("No");
        else puts("Yes");

    }


    return 0;
}

M-Yet Another Stones Game

题意:给你n个权值a[i] 两个人玩,Little Gyro 先手,每次每个人选n/2个数 并且同时去掉任意的值,谁最后无法移动就是输了,若 Little Gyro 赢,输出 Sad Little Gyro 否则输出 Happy Little Gyro

做法:简单分析必胜态必败态。假设n=4 

(0,0,0,0)必败态

(0,0,1,1)必胜态

(1,1,1,1)必败态   

(1,1,1,2)必败态

(1,1,2,2)必胜态

(2,2,2,2)必败态

(2,2,3,3)必胜态

(2,3,4,3)必胜态

由此可以猜测结论,当最小值个数超过n/2 个 先手必败,因为无论你先手选哪n/2 个数,你取多少,我就取另外n/2个数 且一样多的话 使得你依旧处于必败态。

那对于最小值个数小于n/2 个嘞?怎么操作才能必胜?这里的话我还没懂怎么才能必胜,靠上面的猜测盲交一发。A了。。ac is ok 希望能有大佬指正~

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

const int N=1e2;
int n,m;
int a[N];
void solve(){
    cin>>n;
    int ans=0,mi=1e9;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        mi=min(mi,a[i]);
    }
    for(int i=1;i<=n;i++){
        if(mi<a[i])ans++;
    }
    if(ans>=n/2)printf("Happy Little Gyro\n");
    else printf("Sad Little Gyro\n");
}
int main()
{
    int _;cin>>_;while(_--) solve();

}
/*
*/

 

N-Yet Another Hanoi Problem

题意:汉诺塔问题,改动:每次不能直接从a柱 到达c 柱。

做法:之前寒假训练出过两次此类题,补过题应该知道普通的汉诺塔跟二进制 +1 +1 的变化有关,且答案也是 2^n-1

然而这里有改动,其实很简单,就是多了一位变化,那就是3进制下的变化,答案就是3^n-1

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const ll mod=1e9+7;
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
ll powmod(ll a,ll b) {ll res=1;a%=mod;
assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
const int N=1e6+10;
ll f[N];
int n;
int main()
{
    f[0]=1;
    rep(i,1,N-1){
        f[i]=f[i-1]*3%mod;
    }
    int _=read();while(_--)
    {
        n=read();
        printf("%d\n",f[n]-1);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/106531887
今日推荐