【Ctrl C/V Team】Week 1复习赛1 题解

297 阅读11分钟

A题 开心的金明

题目描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1−5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,…,jk,则所求的总和为: image.png 请你帮助金明设计一个满足要求的购物单。

输入格式

第一行,为2个正整数,用一个空格隔开:n,m(其中N(<30000)表示总钱数,m(<25)为希望购买物品的个数。)

从第2行到第m+1行,第j行给出了编号为j−1的物品的基本数据,每行有2个非负整数vp(其中v表示该物品的价格(v≤10000),p表示该物品的重要度(1−5)

输出格式

1个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。

输入输出样例

输入 #1

1000 5
800 2
400 5
300 5
400 3
200 2

输出 #1

3900

说明/提示

NOIP 2006 普及组 第二题

思路

经典DP动态规划+01背包问题。背包是钱的总数,质量为价格 * 重要度(题目说的)。代入模板即可。

AC代码

#include <bits/stdc++.h>
#include <iostream>
using namespace std;

int n,m;
vector<int> level;
vector<int> money;
vector<int> dp;
int main()
{
    cin>>n>>m;
    dp.resize(50000);


    level.resize(m+1);
    money.resize(m+1);

    for(int i=1;i<=m;i++)
    {
        cin>>money[i]>>level[i];
        level[i]*=money[i];//直接在level数组当中存质量
    }
    for(int i=1;i<=m;i++)//模板  m是数量  n是总钱数 
    {
        for (int c = n; c >= money[i]; c--)
        {
            if (c >= money[i])
                dp[c] = max(dp[c], dp[c - money[i]] + level[i]);
        }
    }
    cout<<dp[n]<<endl;//dp[n]存储最后结果
    return 0;
}

题目来源

P1060 [NOIP2006 普及组] 开心的金明

B题 过河卒

题目描述

棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,A 点 (0, 0)、B 点 (n, m),同样马的位置坐标是需要给出的。

现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 B 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

输入输出样例

输入 #1

6 6 3 3

输出 #1

6

说明/提示

对于100%的数据,1 <= n, m <= 20,1≤n,m≤20,0≤马的坐标≤20。

思路

走迷宫问题,第一想法是DFS回溯,将马所在位置和它能一步走到的位置打上标记,相当于迷宫问题中的障碍物,得莫,会超时。所以又想到DP动态规划,将dp数组定义为走到当前坐标所需要的方案数量,由于卒只能向下或者向右走,所以dp[x][y]的值为dp[x-1][y]+dp[x][y-1] (当然前提是x和y合法)。可推出以下状态转移方程:

image.png

AC代码

#include <bits/stdc++.h>
#include <iostream>
using namespace std;

long long int hx, hy, bx, by;
const int maxn = 1000;
long long ans = 0;//方案数量 答案可能会爆int 所以用long long
int dirx[]={0,-2,-1,1,2,2,1,-1,-2};//马的方向
int diry[]={0,1,2,2,1,-1,-2,-2,-1};
bool vis[maxn][maxn];//标记某处坐标是否能走
long long dp[maxn][maxn];//储存走到某处坐标的方案数量

void judge(int x,int y)//将马能走到的地方标记为true
{
    if(x>=0&&y>=0)
        vis[x][y]=true;
}

int main()
{
    cin>>bx>>by>>hx>>hy;
    for(int i=0;i<=8;i++)//获得马走的方向
    {
        judge(hx+dirx[i],hy+diry[i]);
    }
    dp[0][0]=1;//初始化原点的方案数量为1,
    for(int i=0;i<=bx;i++)//遍历,从(0,0)到(bx,by)的点,如果满足条件就判断
    {
        for (int j = 0; j <=by; j++)
        {
            if(vis[i][j])
                continue;
            dp[i][j]=max(dp[i][j],dp[i-1][j]+dp[i][j-1]);
        }
    }
    cout<<dp[bx][by]<<endl;
    return 0;
}

题目来源

P1002 [NOIP2002 普及组] 过河卒

C题 方格取数

题目描述

设有 N×N 的方格图 (N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0。如下图所示(见样例):

A
 0  0  0  0  0  0  0  0
 0  0 13  0  0  6  0  0
 0  0  0  0  7  0  0  0
 0  0  0 14  0  0  0  0
 0 21  0  0  0  4  0  0
 0  0 15  0  0  0  0  0
 0 14  0  0  0  0  0  0
 0  0  0  0  0  0  0  0
                         B

某人从图的左上角的 A 点出发,可以向下行走,也可以向右走,直到到达右下角的 B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0)。
此人从 A 点到 B 点共走两次,试找出 2 条这样的路径,使得取得的数之和为最大。

输入格式

输入的第一行为一个整数 N(表示 N×N 的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 0 表示输入结束。

输出格式

只需输出一个整数,表示 2 条路径上取得的最大的和。

输入输出样例

输入 #1

8
2 3 13
2 6  6
3 5  7
4 4 14
5 2 21
5 6  4
6 3 15
7 2 14
0 0  0

输出 #1

67

说明/提示

NOIP 2000 提高组第四题

思路

这题,是四维动规的模板题,和P1006传纸条基本相似。 我们考虑两个人同时走,就相当于数字三角形。状态转移方程为: image.png 不过要判断i=k&&j=l的情况。

AC代码

 #include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int f[12][12][12][12],a[12][12],n,x,y,z;
int main() {
    cin>>n>>x>>y>>z;
    while(x!=0||y!=0||z!=0){ //如果输入检测到0 0 0就结束输入
        a[x][y]=z;
        cin>>x>>y>>z;
    }
    for(int i=1;i<=n;i++){ //哎呀四维动态规划 就看看就好
        for(int j=1;j<=n;j++){
            for(int k=1;k<=n;k++){
                for(int l=1;l<=n;l++){
                    f[i][j][k][l]=max(max(f[i-1][j][k-1][l],f[i-1][j][k][l-1]),max(f[i][j-1][k-1][l],f[i][j-1][k][l-1]))+a[i][j]+a[k][l];
                    if(i==k&&l==j)f[i][j][k][l]-=a[i][j];
                }
            }
        }
    }
    cout<<f[n][n][n][n];
    return 0;
}

题目来源

P1004 [NOIP2000 提高组] 方格取数

D题 小A点菜

题目背景

uim神犇拿到了uoira(镭牌)后,立刻拉着基友小A到了一家……餐馆,很低端的那种。

uim指着墙上的价目表(太低级了没有菜单),说:“随便点”。

题目描述

不过uim由于买了一些书,口袋里只剩M元(M≤10000)。

餐馆虽低端,但是菜品种类不少,有N种(N≤100),第i种卖ai元(ai≤1000)。由于是很低端的餐馆,所以每种菜只有一份。

小A奉行“不把钱吃光不罢休”,所以他点单一定刚好把uim身上所有钱花完。他想知道有多少种点菜方法。

由于小A肚子太饿,所以最多只能等待11秒。

输入格式

第一行是两个数字,表示N和M。

第二行起N个正数aai(可以有相同的数字,每个数字均在1000以内)。

输出格式

一个正整数,表示点菜方案数,保证答案的范围在int之内。

输入输出样例

输入 #1

4 4
1 1 2 2

输出 #1

3

思路

image.png

AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    vector<int> a,dp;
    int n,m;
    cin>>n>>m;
    dp.resize(10010);
    a.resize(n);
    for(int i=0;i<n;i++)
        cin>>a[i];
    dp[0]=1;
    for(int i=0;i<n;i++)
    {
        for(int j=m;j>=a[i];j--)
        {
            dp[j]+=dp[j-a[i]];//状态转移方程
        }
    }
    cout<<dp[m]<<endl;
    return 0;
}

题目来源

P1164 小A点菜

E题 NASA的食物计划

题目背景

NASA(美国航空航天局)因为航天飞机的隔热瓦等其他安全技术问题一直大伤脑筋,因此在各方压力下终止了航天飞机的历史,但是此类事情会不会在以后发生,谁也无法保证,在遇到这类航天问题时,解决方法也许只能让航天员出仓维修,但是多次的维修会消耗航天员大量的能量,因此NASA便想设计一种食品方案,让体积和承重有限的条件下多装载一些高卡路里的食物.

题目描述

航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里,在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次.

输入格式

第一行 两个数 体积最大值(<400)和质量最大值(<400)

第二行 一个数 食品总数N(<50).

第三行-第3+N行

每行三个数 体积(<400) 质量(<400) 所含卡路里(<500)

输出格式

一个数 所能达到的最大卡路里(int范围内)

输入输出样例

输入 #1

320 350
4
160 40 120
80 110 240
220 70 310
40 400 220

输出 #1

550

思路

01背包。相比一般的01背包,这题多了俩东西。可以用二维数组来解决。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int v,m;
    cin>>v>>m;
    int n;
    cin>>n;
    vector<int> vi,mi,cll;
    vi.resize(n);
    mi.resize(n);
    cll.resize(n);
    vector<vector<int>>dp;
    dp.resize(1000);
    for(int i=0;i<dp.size();i++)
        dp[i].resize(1000);
    for(int i=0;i<n;i++)
        cin>>vi[i]>>mi[i]>>cll[i];
    for(int i=0;i<n;i++)
    {
        for(int j=v;j>=vi[i];j--)//多了俩变量,用二层for循环,格式和模板一样
        {
            for(int k=m;k>=mi[i];k--)
            {
                dp[j][k]=max(dp[j][k],dp[j-vi[i]][k-mi[i]]+cll[i]);
            }
        }
    }
    cout<<dp[v][m]<<endl;
    return 0;
}

题目来源

P1507 NASA的食物计划

F题 礼物的最大价值

题目描述

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

 

输入示例

输入:

1 3 1
1 5 1
4 2 1

输出:

12

解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物  

说明

0 < grid.length <= 200
0 < grid[0].length <= 200

思路

动态规划。我又经典往dfs想,依然无语。这个题目杭电入门课讲动态规划有类似的题目。将dp[i][j]定义为在(i,j)处拿到的最大价值。可得状态转移方程:

(~O3YDD4J1)99DA998S%E$9.png

AC代码

#include<bits/stdc++.h>
using namespace std;

int maxValue(vector<vector<int>>& grid) {//转载自:1146
    int m=grid.size();
    int n=grid[0].size();//分别求得二维数组vector的行数,列数

    for(int i=0;i<m;i++)//双层for循环遍历每一个元素
    {
        for(int j=0;j<n;j++)
        {
            if(i==0&&j==0) continue;
            //判断边界,第一个元素无需遍历

            if(i>0&&j>0) grid[i][j]+=max(grid[i-1][j],grid[i][j-1]);
                //如果该元素不在第一行或第一列,则该元素加上左边元素和上面元素中较小的

            else if(i==0&&j!=0) grid[i][j]+=grid[i][j-1];
                //如果该元素在第一行,则没有上面的元素,只能加上左边的元素

            else grid[i][j]+=grid[i-1][j];
            //如果该元素在第一列,则没有左边的元素,只能加上面的元素
        }
    }
    return grid[m-1][n-1];
    //最后返回数组右下角的元素即为最小路径和
}

int main()
{
    vector<vector<int>> grid;
    int n,m;
    cin>>n>>m;
    grid.resize(n);
    for(int i=0;i<grid.size();i++)
        grid[i].resize(m);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cin>>grid[i][j];
        }
    }
    int ans=maxValue(grid);
    cout<<ans<<endl;
    return 0;
}

题目来源:

剑指 Offer 47. 礼物的最大价值

G题 螺旋矩阵 Ⅲ

题目描述

在 R 行 C 列的矩阵上,我们从 (r0, c0) 面朝东面开始 这里,网格的西北角位于第一行第一列,网格的东南角位于最后一行最后一列。 现在,我们以顺时针按螺旋状行走,访问此网格中的每个位置。 每当我们移动到网格的边界之外时,我们会继续在网格之外行走(但稍后可能会返回到网格边界)。 最终,我们到过网格的所有 R * C 个空间。 按照访问顺序返回表示网格位置的坐标列表。

输入格式

输入一行四个整数,分别为R、C、r0、c0。

输出格式

一行由形如[x,y]组成的点集,每个点之间用“,”逗号隔开。

示例 1:

输入:

1 4 0 0

输出:

[0,0],[0,1],[0,2],[0,3]

示例 2:

输入:

5 6 1 4

输出:

[1,4],[1,5],[2,5],[2,4],[2,3],[1,3],[0,3],[0,4],[0,5],[3,5],[3,4],[3,3],[3,2],[2,2],[1,2],[0,2],[4,5],[4,4],[4,3],[4,2],[4,1],[3,1],[2,1],[1,1],[0,1],[4,0],[3,0],[2,0],[1,0],[0,0]

提示:

1 <= R <= 100
1 <= C <= 100
0 <= r0 < R
0 <= c0 < C

思路

模拟题。注意边界该如何处理比较好。

AC代码

#include<bits/stdc++.h>
using namespace std;
vector<vector<int>> num;
void spiralMatrixIII(int rows, int cols, int rStart, int cStart) {

    int dir=1;//1 right 2 down 3 left 4 up
    int step=0;
    num.resize(rows*cols);
    for(int i=0;i<num.size();i++)
        num[i].resize(2);
    int k=1;
    num[0][0]=rStart;
    num[0][1]=cStart;
    long long int t1=rStart,t2=cStart;
    while(k<rows*cols)
    {

        if(dir==1)
        {
            step++;
            for(int i=1;i<=step;i++)
            {
                t2++;
                if(t1>=0&&t1<rows&&t2>=0&&t2<cols)
                {
                    num[k][0]=t1;
                    num[k][1]=t2;
                    k++;
                }
            }
        }else if(dir==2)
        {
            for(int i=1;i<=step;i++)
            {
                t1++;
                if(t1>=0&&t1<rows&&t2>=0&&t2<cols)
                {
                    num[k][0]=t1;
                    num[k][1]=t2;
                    k++;
                }
            }
        }else if(dir==3)
        {
            step++;
            for(int i=1;i<=step;i++)
            {
                t2--;
                if(t1>=0&&t1<rows&&t2>=0&&t2<cols)
                {
                    num[k][0]=t1;
                    num[k][1]=t2;
                    k++;
                }
            }
        }else if(dir==4)
        {
            for(int i=1;i<=step;i++)
            {
                t1--;
                if(t1>=0&&t1<rows&&t2>=0&&t2<cols)
                {
                    num[k][0]=t1;
                    num[k][1]=t2;
                    k++;
                }
            }
        }
        dir++;
        if(dir==5)
            dir=1;
    }

}
int main()
{
    int r,c,r0,c0;
    cin>>r>>c>>r0>>c0;
    //cout<<r<<c<<r0<<c0;
    vector<vector<int>> ans;
    spiralMatrixIII(r,c,r0,c0);
    for(int i=0;i<num.size();i++)
    {
        cout<<"["<<num[i][0]<<","<<num[i][1]<<"]";//Print
        if(i!=num.size()-1)
            cout<<",";
    }
    return 0;
}

题目来源

885. 螺旋矩阵 III - 力扣(LeetCode) (leetcode-cn.com)

H题 【不给你看哦】:Text Reverse

Problem Description【English Version】

Ignatius likes to write words in reverse way. Given a single line of text which is written by Ignatius, you should reverse all the words and then output them.

Input

The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow. Each test case contains a single line with several words. There will be at most 1000 characters in a line.

Output

For each test case, you should output the text which is processed.\

Sample Input

3
olleh !dlrow
m'I morf .udh
I ekil .mca

Sample Output

hello world!
I'm from hdu.
I like acm.

Hint

Remember to use getchar() to read '\n' after the interger T, then you may use gets() to read a line and process it.

英文知识小贴士

为了以后的CF做准备!冲啊!【以下翻译的意思仅为部分】

reverse:v. 逆转,彻底改变;n. 相对,相反;adj. 相反的,反向的;\ case:n. 具体情况,实例;v. 围绕,包盖;
integer:n. 整数;完整物,统一体;

问题描述【中文版】

伊格内修斯喜欢以相反的方式写字。给定一行由依纳爵写的文字,你应该反转所有的单词,然后输出它们。

输入

输入包含多个测试用例。输入的第一行是单个整数 T,它是测试用例的数量。T 测试用例如下。每个测试用例都包含一行包含多个单词。一行最多有 1000 个字符。

输出

对于每个测试用例,应输出已处理的文本。

示例输入

3
olleh !dlrow
m'I morf .udh
I ekil .mca

示例输出

hello world!
I'm from hdu.
I like acm.

提示

请记住使用 getchar() 在中间函数 T 之后读取 '\n',然后可以使用 gets() 读取一行并对其进行处理。

思路

本题难度不大,一个小小模拟题,稍微要脑子的有两处,一个是翻译原文,一个是这题的翻转文本不是整个翻转,而是翻转单词,也就是要判断空格的存在。没啥别的可说,放代码。

AC代码

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n;//n是要输入的字符串的行数
    cin>>n;
    getchar();
    string s;
    vector<string> ans;
    ans.resize(n);
    for(int i=0;i<n;i++)
    {
        int start=0,end=0;//start代表一个单词的开始位置,end则为结束位置或者说是空格位置
        getline(cin,s);//利用getline()把空格给读取进去,注意cin是碰到空格则停止
        for(int j= start;j<s.size()+1;j++)//此处循环到s.size()+1是因为要判断最后一个单词
        {

            if(s[j]==' '||s[j]=='\0')//s[j]=='\0'是到了最后一个单词
            {
                end=j;
                reverse(s.begin()+start, s.begin()+end);//reverse()翻转字符串函数
                start=end+1;//将start置为空格的下一个位置,也就是下一个单词的开始
                continue;
            }
        }
        ans[i]=s;//全部转换完了存到ans数组里

    }
    for(int i=0;i<n;i++)//Print
        cout<<ans[i]<<endl;
    return 0;
}

题目来源

Problem - 1062 (hdu.edu.cn)

//2022.4.15 【Ctrl C/V Team】Week 1复习赛1题解