河工软院ACM集训12/15

A.输出简单的Z

知识点:模拟

通过观察图形,不难看出除第一行和最后一行外,其余行均由n - 1个 '.' 和一个  '/' 组成

而第二行 '/'的位置又恰好在n - 1处,第三行在n - 2处,第四行在n - 3处,于是不难推出,第x行的斜杠在n - x + 1处(下标从1开始)

那么,我们可以定义一个二维字符数组,先全部初始化为 '.',遍历从第2行到第 n - 1 行,将其对应下标处的 '.' 修改成 '/'。

然后分开输出首行,中间行和末行即可

(ps:本题为多实例,但题面没有要求每两个实例输出之间用换行隔开,所以千万不要在输出后加换行隔开,别问为什么,问就是WA了

代码实现如下:

#include <iostream>
#include <cstring>

using namespace std;

const int N = 100;
int n;
char g[N][N];

int main()
{
	int t;
	scanf("%d",&t);
	
	while(t --)
	{
		scanf("%d",&n);
		memset(g, '.', sizeof g);
        
        for(int i = 1; i < n; i ++) printf("-");
        puts(">");
        
        for(int i = 2; i < n; i ++)
        {
            g[i][n - i + 1] = '/';
            for(int j = 1; j <= n; j ++)
            {
                printf("%c", g[i][j]);
            }
            puts("");
        }
        
        printf("<");
        for(int i = 1; i < n; i ++)
        {
            printf("-");
        }
        
        puts("");
        // puts(""); 别加这一行,加了就是20分钟
	}
    return 0;
}

B.失眠就数羊

知识点:简单数位拆分,标记数组(可以不用)

定义w数组存储数字0~9的圈的个数(1, 0, 0, 0, 1, 0, 1, 0, 2, 1)
将a到b之间的每一个数进行数位拆分,求得每个数字各位圈的个数的和(建议函数实现)

将上述函数的返回值相加输出即可

tabris笨不笨我不清楚,我只知道我写这题这题数圈数老半天,给自己都数迷糊了(ps:确实不理解4这个三角为什么也算圈儿)

代码实现如下:

#include <iostream>

using namespace std;

const int N = 100;
int n, q[N];
int w[10] = {1, 0, 0, 0, 1, 0, 1, 0, 2, 1};

int check(int x)
{
    int res = 0;
    while(x > 0)
    {
        res += w[x % 10];
        x /= 10;
    }
    return res;
}

int main()
{
	int t, a, b;
	scanf("%d",&t);
	
    long long ans;
	while(t --)
	{
		scanf("%d %d",&a, &b);
        
        ans = 0;
		for(int i = a; i <= b; i ++)
        {
            ans += check(i);
        }
        
        printf("%lld\n", ans);
	}
}

C.简单的求和

知识点:前缀和

多次查询区间和,自然要祭出我为数不多会用的算法之一——前缀和 来写(虽然有同学反馈用scanf 枚举求和 也能通过,但大概率是数据不够严谨,小问题,前缀和该学还得学)

所谓前缀和,可以简单理解为高中所学的数列的前 n 项和,那么,这个前缀和有什么用呢?

且看本题,多次查询数列区间和,我们以数据范围的上限来计算一波:

数列最长有1e5项,最多有1e5次询问,每次询问最长需要求长为1e5的区间的和,那么计算之后,得到最大操作次数的数量级为1e10(1e5次操作 * 每次操作的1e5个数相加)

众所周知,当题目给定的时间限制为1s时,最大操作次数的数量级大概在1e8(10的8次方),所以直接去暴力枚举是会TLE(超时)的。

这时候,我们的前缀和就派上用场了:

在读入原数组之后,我们定义一个新的数组(或在原数组上进行操作),使得数组下标为 i 处存储元素为从数组第1项到第 i 项的和,如此便得到了前缀和数组。

当我们需要求闭区间区间 l 到 r 的和时,只需用 前缀和数组的第 r 项(即数列前 r 项的和)减去前缀和数组的第 l - 1项 (即数列前 l - 1项的和),得到的差便是所求。

代码实现如下:

#include <iostream>

using namespace std;

const int N = 2e5 + 20;
int n;
long long q[N], w[N];

int main()
{
    int m;
    scanf("%d %d",&n,&m);
    
    for(int i = 1; i <= n; i ++)
    {
        scanf("%lld",&q[i]);
//      w[i] = w[i - 1] + q[i]; //以 w 数组作为原数组的前缀和数组
        q[i] += q[i - 1]; //在原数组上操作得到前缀和数组
    }
    
    while(m --)
    {
        int x, y;
        scanf("%d %d",&x, &y);
        
//      printf("%lld\n", w[y] - w[x - 1]);
        printf("%lld\n", q[y] - q[x - 1]);
    }
    return 0;
}

D.沉迷游戏

知识点:排序(外加一点小思维)

观察题给数据范围,可以发现,至少要玩的游戏数量是一定会大于特别喜欢玩的游戏数量的

于是我们可以将最喜欢玩的游戏视作必须要玩的游戏,在读入它们的编号时直接求得它们耗时的和,并将其耗时归零。

然后将整个数组排序(由于特别喜欢的游戏耗时已经归零,它们一定会在排序后的数组的最前面)

对于排序后的数组,直接求前k项和即可

代码实现如下:

#include <iostream>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int N = 1010;
int n, q[N];

int main()
{
    int m, k, x, w = 0;
    scanf("%d %d %d",&n,&m,&k);
    
    for(int i = 1; i <= n; i ++)
    {
        scanf("%d",&q[i]);
    }
    
    while(m --)
    {
        scanf("%d",&x);
        w += q[x];
        q[x] = 0;
    }
    
    sort(q + 1, q + n + 1);
    
    for(int i = 1; i <= k; i ++)
    {
        w += q[i];
    }
    
    printf("%d", w);
    return 0;
}

E.X型不明物体

知识点:贪心

(有一说一,题目中的型应写作“形”,这个型看起来一点都不行)

对于任给n个 'X' 形图案,我们可将其看作两条直线,那么,对于一个平面,没有线时,它是一个完整的区域,有一条线时,它被分为了两部分,有两条线时,为了让划分的区域最多,我们让两条线相交,便可以将平面划分为四部分。

对于平面内任意多的已有直线,我们总有办法将新增直线以不与其他线平行,且不穿过其他线交点的形式加入到平面中,假设原有 x 条直线以这种方式存在在平面中,那么新增直线后,平面内会多出 x + 1 条直线。

于是,对于 n 个'X'形图案,我们可以将其视作 2 * n条直线,并将加入平面的过程其拆分,可得到最终划分区域数量的计算公式:

                                划分区域数量 = 1 + 1 + 2 + ... + 2 * n - 1 + 2 * n

将从1加到2 * n的部分提取出来,用等差数列前n项和公式替换,便得到 1 + n * (n + 1) / 2

代码实现如下:

#include <iostream>

using namespace std;

long long n;

int main()
{
    scanf("%lld", &n);
    
    n *= 2;
    
    printf("%lld", n * (n + 1) / 2 + 1);
    return 0;
}

F.迷失的他

知识点:BFS

时间不太够,废话待会儿再添,先上代码

代码实现如下:

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

const int N = 100;

typedef pair<int, int> PII;
int n, m, ans, cnt = 10000, w[N][N];
char g[N][N];

void bfs(int a, int b)
{
    queue<PII> q;
    q.push({a, b});
    
    w[a][b] = 0;
    
    int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
    
    while(!q.empty())
    {
        PII t = q.front();
        q.pop();
        
        if(g[t.first][t.second] == 'e')
        {
            ans ++;
            cnt = min(w[t.first][t.second], cnt);

            continue;
        }

        for(int i = 0; i < 4; i ++)
        {
            int x = t.first + dx[i], y = t.second + dy[i];
            
            
            
            if(x > 0 && x <= n && y > 0 && y <= m && w[x][y] == -1 && g[x][y] != '*')
            {
                w[x][y] = w[t.first][t.second] + 1;
                q.push({x, y});
            }
        }
    }
}

int main()
{
    int x, y;
    scanf("%d %d\n",&n,&m);
    
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++)
        {
            scanf("%c",&g[i][j]);
            if(g[i][j] == 'k')
            {
                x = i;
                y = j;
            }
        }
        getchar();
    }
    
    memset(w, -1, sizeof(w));
    
    bfs(x, y);
    
    if(ans) printf("%d %d", ans, cnt);
    else puts("-1");
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值