2023 ACM-WEEK1

T1.特殊的正方形

思路:

模拟题

0.一开始没想到,感觉c++好像只能一行接着一行输出啊😶然后尝试找每一行符号的规律...嗯..没找出来,后来才发现其实可以先开矩阵再往里填东西的!!

  1. 先构建一个空的char矩阵,然后就转化成往这个矩阵里填对应的字符

  1. 然后就是从外到里一圈一圈填字符,总共填n-n/2次(不是n/2次)

  1. 写的时候注意下标

代码:

#include<bits/stdc++.h>
using namespace std;
int n;
char fil,ans[105][105];
int main()
{
    //memset(ans,'#',sizeof(ans));
    cin>>n;
    int tot=n-n/2;//有几个圈,不是n/2!
    for(int i=1;i<=tot;i++)
    {   
        int stX=i,stY=i;
        if(i%2!=0)fil='+';//奇数圈
        else fil='.';
        for(int j=stX;j<=n-2*(i-1)+stX-1;j++)ans[j][i]=fil;
        for(int j=stX;j<=n-2*(i-1)+stX-1;j++)ans[j][n+1-i]=fil;//填充x
        for(int j=stY;j<=n-2*(i-1)+stY-1;j++)ans[i][j]=fil;
        for(int j=stY;j<=n-2*(i-1)+stY-1;j++)ans[n+1-i][j]=fil;//填充y
    }//注意下标!!
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            cout<<ans[i][j];
        cout<<endl;
    }
}

T2.简单分数统计

思路:

模拟题,按着题目要求一步一步写就ok啦

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,k,scoreT[205],score[205];
string id[205],nameT[205],isPass[205];
int main()
{
    cin>>n>>m>>k;//n个人 m道题 k次提交
    for(int i=1;i<=n;i++)cin>>id[i];
    for(int i=1;i<=m;i++)cin>>nameT[i]>>scoreT[i];
    for(int i=1;i<=k;i++)
    { 
        string namet,ans;
        bool flag=false;
        int which;
        string userID;
        cin>>userID>>namet>>ans;
        for(int j=1;j<=n;j++)
            if(id[j]==userID)
            {
                flag=true;
                which=j;
            }
        if(!flag)continue;
        for(int j=1;j<=m;j++)
            if(nameT[j]==namet)
                if(ans=="AC")
                    score[which]+=scoreT[j];
    }
    for(int i=1;i<=n;i++)
        cout<<id[i]<<' '<<score[i]<<endl;
}

T3.Alice的德州扑克

思路:

也是模拟题,就是题目比较长

  1. 根据题目要求写出判断不同牌型的函数即可

  1. 比较讨厌的是葫芦的判定,用的方法是先选出两张同样花色的,再判断剩下三张花色是否相同

代码:

#include<bits/stdc++.h>//细节...注意一点😭
using namespace std;
int point[6],color[5];
bool same_color()
{
    bool flag=true;
    for(int i=1;i<5;i++)
        if(color[i]!=color[i+1])
            flag=false;
    return flag;
}
bool increase()
{
    bool flag=true;
    for(int i=1;i<5;i++)
        if(point[i]!=point[i+1]-1)
            flag=false;
    return flag;
}
bool ST()
{
    bool flag=false;
    for(int i=1;i<=5;i++)
    {
        int tot=0;
        for(int j=1;j<=5;j++)
        {
            if(j==i)continue;
            if(point[i]==point[j])
                tot++;
        }
        if(tot>=3)
        {
            flag=true;
            break;
        }
    }
    return flag;
}
bool HL()
{
    bool flag=false;
    //先选两张点数相同的牌(2+3)
    for(int i=1;i<=5;i++)
        for(int j=1;j<=5;j++)
        {
            if(j==i)continue;
            if(point[i]==point[j])//再看剩下三张
            {
                int p;
                flag=true;
                bool flag2=false;
                for(int k=1;k<=5;k++)
                {
                    if((k==i)||(k==j)||flag2==true)continue;
                    p=point[k];//选基准点
                    flag2=true;
                }
                for(int k=1;k<=5;k++)
                {
                    if(k==i||k==j)continue;
                    if(point[k]!=p)
                        flag=false;
                }
            }
            if(flag) return true;
        }
    return false;
}
int main()
{
    for(int i=1;i<=5;i++)cin>>point[i];
    for(int i=1;i<=5;i++)cin>>color[i];
    if(same_color()&&increase()&&point[5]==14)cout<<"ROYAL FLUSH";
    else if(same_color()&&increase())cout<<"STRAIGHT FLUSH";
    else if(ST())cout<<"FOUR OF A KIND";
    else if(HL())cout<<"FULL HOUSE";
    else if(same_color())cout<<"FLUSH";
    else if(increase())cout<<"STRAIGHT";
    else cout<<"FOLD";
}

T4.走路

思路:

p.s这题样例看了好久,后来才发现所谓能到达指的是最后到达的位置,不算中间经过的点

  1. 看到题目其实一开始想到的是dfs,搜索每一个可能走到的位置,然后就TEL了😭

  1. 考虑dp:

这里定义二维数组dp[i][j],表示第i步能否走到第j阶

由于第0步一定在第0阶位置,所以初始值为dp[0][0] = 1

关于状态转移方程:

第i步的状态可以通过第i-1步的状态推出来

如果第i-1步可以到第j阶,那么只需要在判断一下j+a[i]与j+b[i]是否小于m就可以确定能否到达了

代码(dp和dfs):

#include<bits/stdc++.h>
using namespace std;
int n,m,a[105],b[105],dp[105][100005];//dp[i][j]走了i步后,在第j个位置上
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i]>>b[i];
    dp[0][0]=1;
    for(int i=1;i<=n;i++)//第几步
        for(int j=0;j<=m;j++)//遍历这一步可能走到的情况
        {
            if(dp[i-1][j]==1&&j+a[i]<=m)//根据上一步的位置判断
                dp[i][j+a[i]]=1;
           if(dp[i-1][j]==1&&j+b[i]<=m)
                dp[i][j+b[i]]=1;
        }
    for(int i=0;i<=m;i++)
        cout<<dp[n][i];
}

#include<bits/stdc++.h>
using namespace std;
int n,m,a[105],b[105],ans[100005];
void dfs(int now,int cnt)
{
    if(cnt>n+1) 
    {
        ans[now]=1;
        return;
    }
    dfs(now+a[cnt],cnt+1); 
    dfs(now+b[cnt],cnt+1);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i]>>b[i];
    dfs(0,0);
    for(int i=0;i<=m;i++)
        cout<<ans[i];
}//TLE

T5.走楼梯2

思路:

  1. dp题

  1. 当站在第 k 阶时,要考虑三种情况:

一步一阶登上第 k 阶

一步两阶,且第一次是一步两阶

一步两阶,且连续两次是一步两阶

根据这三种情况就有三种不同的递推关系:

(1)当前走了1步,到达此处的方式不受限制

(2)当前走了2步,到达此处的方式不受限制

(3)当前走了4 步,所以我们只能通过走1 步的方式到达此处

代码:

#include<bits/stdc++.h>
using namespace std;
long long n,dp[55][3];//dp[i][j]现在在第n阶,最近的连续j步走了两阶
int main()
{
    int n;
    cin>>n;
    dp[0][0]=1;
    for(int i=0;i<=n;i++)//我为人人型递推
        for(int j=0;j<=2;j++)
        {
            dp[i+1][0]+=dp[i][j];//这一步走1级台阶
            if(j<2)dp[i+2][j+1]+=dp[i][j];//走两阶
        }
    cout<<dp[n][0]+dp[n][1]+dp[n][2];
}

T6.订单编号

思路:

没思路qwq...看了答案也不是很明白为啥

摘了一段大佬的评论:

这是一个套路题,这个题相当于是一个无限更新的题,

那么我们需要用固定的长度区间去来往里面添加元素,

我们可以把单点操作变成区间查询。我们可以利用set的自动排序来找,

但是插入区间时需要倒着插,因为正着插无法找到大于等于x的数,

例如刚开始时只有一个区间,他就根本查不到大于等于x的区间。

set用法:set排序遇到pair,lower_bound查的是set的first,

不会查second,除非first相等才会按照second来排。

查到的时候只需要观察是否在区间内即可,

(如果区间的最小值大于等于x就直接输出x,如果区间最小值大于x就输出区间最小值)

查到之后将该点去掉分成两个区间。插入pair时需要用insert,并且带上大括号{}。

代码:

#include<bits/stdc++.h>
using namespace std;
set<pair<int,int>>s;
int n;
void myinsert(int l,int r)
{
    if(l>r)return;
    else s.insert({r,l});
}
int main()
{
    ios_base::sync_with_stdio(false);//
    cin.tie(NULL);//硬砍了属于是
    cin>>n;
    s.insert({2e9,1});
    for(int i=1;i<=n;i++)
    {
        int tmp;
        cin>>tmp;
        auto iter=s.lower_bound(make_pair(tmp,0));
        if(iter->second<=tmp)
        {
            cout<<tmp<<' ';
            myinsert(iter->second,tmp-1);
            myinsert(tmp+1,iter->first);
            s.erase(iter);
        }
        else
        {
            cout<<iter->second<<' ';
            myinsert(iter->second+1,iter->first);
            s.erase(iter);
        }
    }
}

T7.一个小整数

思路(没写出来,看的别人题解www):

考虑四种情况:

1、数量最多的那个数,数量是其它数的总和还多1以上

2、数量最多的那个数,数量是其它数的总和还多1

3、数量最多的那个数,数量正好等于其它数的总和

4、数量最多的那个数,数量小于其它数的总和

(以下最多数量的数我们简称x,其它数量的数简称y)

如果是第一种情况,那么显然无论我们怎么样,必然会有相邻的数相同的情况出现,直接输出-1。

如果是第二种情况,那么第一个数只能是x,然后一个y,一个x这样交替放,并且末尾也会是这个最多数,即:xyxy……xyx。

如果是第三种情况,那么第一个数可以是x也可以是y,即:xyxy……xy或yxyx……yx这样子。

如果是第四种情况,我们可以随便摆,只要保证相邻数不相同即可。

情况分析完毕,我们开始想题:

我们每次先遍历一遍这十个数,计算一下数量最多的数和其它数量数总和的关系,然后我们根据关系来取数:

如果是第一种情况,我们直接输出-1结束程序即可;如果是第二种情况,我们第一个数只能是这个最多数量的数;如果是第三、四种情况,我们可以取这十个数中最小的那个数(不强迫选,那为了数最小,我们肯定要选小的数放在前面),但要注意不能和上一个数相同。而且对于第2、3、4种情况,如果这是我们取的第一个数,那么这个数不能是0,因为除了0本身我们不能有前导0(建议一开始特判一下只有一个0,其它数都没有的情况),如果我们必须要取0了,那也是输出-1。

取完一个数后,我们把那个数的个数-1,然后回到开头进行下一步。

数的总个数是2e4,我们每次取一个数要遍历一下这十个数,然后就可以根据情况判断了,总复杂度也才1e5。

代码:

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n'
#define int ll
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
ll n, v[10];

bool dfs(vector<int>& res)
{
    int sum = 0, mx = 0, ans = -1;
    for (int i = 0; i < 10; i++)
    {
        sum += v[i];
        if (v[i] > mx)
        {
            mx = v[i];
            ans = i;
        }
    }
    if (sum == 0)return true;
    sum -= mx;
    
    if (sum + 1 == mx)
    {
        if (ans == 0 && res.empty())
        {
            return false;
        }
        else
        {
            res.push_back(ans);
            v[ans]--;
        }
    }
    else if (sum + 1 < mx)
    {
        return false;
    }
    else
    {
        int u = res.size() == 0 ? 1 : 0;
        int k = u == 1 ? -1 : res.back();
        for (int i = u; i < 10; i++)
        {
            if (v[i] != 0 && i != k)
            {
                res.push_back(i);
                v[i]--;
                break;
            }
        }
    }
    return dfs(res);
}

signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int sum = 0;
    for (int i = 0; i < 10; i++)
        cin >> v[i], sum += v[i];
    if (v[0] == 1 && sum == 1)
    {
        cout << 0 << endl;
        return 0;
    }
    vector<int>res;
    if (dfs(res))
    {
        if (res.empty())cout << -1;
        else for (auto i : res)cout << i;
            cout << endl;
    }
    else
    {
        cout << -1 << endl;
    }

    return 0;
}

T8.饿饿,饭饭

思路:

  1. 二分查找

  1. 看到k范围就知道普通的遍历肯定要超时,考虑二分答案

  1. 我们在不超过k的情况下能打多少轮饭,采取二分的方式确定。这样对于剩下的最后一轮,我们再进行模拟即可

  1. 注意函数形参和传入的实参以及全局变量定义时候的范围要一致,不然可能出现奇怪的错误(比如这题我就被判runtime error找了好久才发现)

代码:

#include<bits/stdc++.h>
using namespace std;
long long n,k,sum,preT,a[100005];
queue<long long>q;
long long calc(long long tot)//形参变量类型要对应!
{
    long long cnt=0;
    for(long long i=1;i<=n;i++)
    if(a[i]<=tot) cnt+=a[i];
    else cnt+=tot;
    return cnt;
}
void binary_search()
{
    long long l=0,r=1e14;
    while(l<=r)
    {
        long long mid=(l+r)/2;
        if(calc(mid)<=k)l=mid+1;
        else r=mid-1;
    }
    preT=r;
}
int main()
{
    cin>>n>>k;
    for(long long i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    if(k>sum)
    {
        cout<<"-1";
        return 0;
    }
    binary_search();//查找最后一轮前还能打几轮
    k-=calc(preT);//先更新k(不然后面a[i]变了)
    for(long long i=1;i<=n;i++)//初始化打完preT轮饭之后的队列
    {
        a[i]-=preT;
        if(a[i]>0)q.push(i);
    }
    for(long long i=1;i<=k;i++)
    {
        long long tmp=q.front();
        if(a[tmp]>0)a[tmp]--;
        q.pop();
        if(a[tmp]>0)q.push(tmp);
    }
    while(!q.empty())
    {
        cout<<q.front()<<' ';
        q.pop();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值