寒假每日一题(五)


前言

又来补题了!这都快暑假了哈


一、开心的金明(背包问题)

题目链接
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。

更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。

今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 N 元。

于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1∼5 表示,第 5 等最重要。

他还从因特网上查到了每件物品的价格(都是整数元)。

他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第 j 件物品的价格为 v[j],重要度为 w[j],共选中了 k 件物品,编号依次为 j1,j2,…,jk,则所求的总和为:

v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]
请你帮助金明设计一个满足要求的购物单。

输入格式
输入文件的第 1 行,为两个正整数 N 和 m,用一个空格隔开。(其中 N 表示总钱数,m 为希望购买物品的个数)

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

输出格式
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(数据保证结果不超过 108)。

数据范围
1≤N<30000,
1≤m<25,
0≤v≤10000,
1≤p≤5
输入样例:
1000 5
800 2
400 5
300 5
400 3
200 2
输出样例:
3900

非常经典的背包问题,“颜式”dp分析法,要考虑状态表示(集合,从前i个数中选,且总体积不超过j的所有选法的集合,属性,最大值),状态计算。
第i个元素分为选i还是不选i,然后选取这两种情况的最大值。不选i,就是1~i-1中选,价值不超过的j-vi最大值

整个是O(n),接下来是

背包问题不做优化空间的做法。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=30,M=30010;

int n,m;
int f[N][M];

int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    {
        int v,w;
        cin>>v>>w;
        w=v*w;
        for(int j=0;j<=m;j++)
        {
            f[i][j]=f[i-1][j];//不选i的情况一定存在
            if(j>=v)
            {
                f[i][j]=max(f[i][j],f[i-1][j-v]+w);//这就是在选择i与不选择i之间做出选择
            }
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

空间优化后(还是小迷糊)

就是全把二维数组转化为一维数组,然后再将式子转化为等价的式子。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=30,M=30010;

int n,m;
int f[M];

int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    {
        int v,w;
        cin>>v>>w;
        w=v*w;
        for(int j=m;j>=v;j--)
        {
     //f[j]=f[j];由于这一行是恒等式,所以可以去掉
    f[j]=max(f[j],f[j-v]+w);//那么j-v就会在之后求出来
        }
    }
    cout << f[m] << endl;
    return 0;
}

二、数独检查

数独是一种流行的单人游戏。

目标是用数字填充 9×9 矩阵,使每列,每行和所有 9 个非重叠的 3×3 子矩阵包含从 1 到 9 的所有数字。

每个 9×9 矩阵在游戏开始时都会有部分数字已经给出,通常有一个独特的解决方案。

在这里插入图片描述

在这里插入图片描述

给定完成的 N2×N2 数独矩阵,你的任务是确定它是否是有效的解决方案。

有效的解决方案必须满足以下条件:

每行包含从 1 到 N2 的每个数字,每个数字一次。
每列包含从 1 到 N2 的每个数字,每个数字一次。
将 N2×N2 矩阵划分为 N2 个非重叠 N×N 子矩阵。 每个子矩阵包含从 1 到 N2 的每个数字,每个数字一次。
你无需担心问题的唯一性,只需检查给定矩阵是否是有效的解决方案即可。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含整数 N。

接下来 N2 行,每行包含 N2 个数字(均不超过 1000),用来描述完整的数独矩阵。

输出格式
每组数据输出一个结果,每个结果占一行。

结果表示为 Case #x: y,其中 x 是组别编号(从 1 开始),如果给定矩阵是有效方案则 y 是 Yes,否则 y 是 No。

数据范围
1≤T≤100,
3≤N≤6
输入样例:
3
3
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
3
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
3
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 999 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
输出样例:
Case #1: Yes
Case #2: No
Case #3: No

数独在算法中也是一大问题,这背后还有一个算法DLX,可以用来解决数独问题也可以解决八皇后问题。,每一行、每一列、每一个小方格1~9只出现一次。
可以开一1~n^2的bool数组,标记x,此题就是为了加深对数独的理解,感觉这个就是每一行去找,每一列,每一个小方格,考一点点小技巧,思维而已,并没有什么特别通用的。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=40;

int n,m;
int w[N][N];
bool st[N];

bool check_row()
{
    for(int i=0;i<m;i++)
    {
        memset(st,0,sizeof st);
        for(int j=0;j<m;j++)
        {
            int t=w[i][j];
            if(t<1||t>m)
                return false;
            if(st[t])return false;
            st[t]=true;
        }
    }
    return true;
}

bool check_col()
{
    for(int i=0;i<m;i++)
    {
        memset(st,0,sizeof st);
        for(int j=0;j<m;j++)
        {
            int t=w[j][i];
            if(t<1||t>m)
                return false;
            if(st[t])return false;
            st[t]=true;
        }
    }
    return true;
}
bool check_cell()
{
    for(int x=0;x<m;x+=n)//这个要掌握一个小技巧0、3、6,一个小方格一个小方格的去找
        for(int y=0;y<m;y+=n)
    {
        memset(st,0,sizeof st);
        for(int dx=0;dx<n;dx++)
            for(int dy=0;dy<n;dy++)
        {
            int t=w[x+dx][y+dy];
            if(t<1||t>m)return false;
            if(st[t])return false;
            st[t]=true;
        }
    }
    return true;
}
int main()
{
   int T;
   scanf("%d",&T);
   for(int c=1;c<=T;c++)
   {
       scanf("%d",&n);
       m=n*n;
       for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
        scanf("%d",&w[i][j]);
       if(check_row()&&check_col()&&check_cell())//检查行 检查列 检查每个小方块
        printf("Case #%d: Yes",c);
        else printf("Case #1: No",c);

   }
}

三、献给阿尔吉侬的花束(迷宫问题,bfs)

题目链接
阿尔吉侬是一只聪明又慵懒的小白鼠,它最擅长的就是走各种各样的迷宫。

今天它要挑战一个非常大的迷宫,研究员们为了鼓励阿尔吉侬尽快到达终点,就在终点放了一块阿尔吉侬最喜欢的奶酪。

现在研究员们想知道,如果阿尔吉侬足够聪明,它最少需要多少时间就能吃到奶酪。

迷宫用一个 R×C 的字符矩阵来表示。

字符 S 表示阿尔吉侬所在的位置,字符 E 表示奶酪所在的位置,字符 # 表示墙壁,字符 . 表示可以通行。

阿尔吉侬在 1 个单位时间内可以从当前的位置走到它上下左右四个方向上的任意一个位置,但不能走出地图边界。

输入格式
第一行是一个正整数 T,表示一共有 T 组数据。

每一组数据的第一行包含了两个用空格分开的正整数 R 和 C,表示地图是一个 R×C 的矩阵。

接下来的 R 行描述了地图的具体内容,每一行包含了 C 个字符。字符含义如题目描述中所述。保证有且仅有一个 S 和 E。

输出格式
对于每一组数据,输出阿尔吉侬吃到奶酪的最少单位时间。

若阿尔吉侬无法吃到奶酪,则输出“oop!”(只输出引号里面的内容,不输出引号)。

每组数据的输出结果占一行。

数据范围
1<T≤10,
2≤R,C≤200
输入样例:
3
3 4
.S…
###.
…E.
3 4
.S…
.E…

3 4
.S…

…E.
输出样例:
5
1
oop!

经典的bfs,第一次搜到的一定是最短路径。
求最短的路径,bfs,如果只是求连通块,就用floodfill(既可以用bfs,也可以用dfs),dfs就比较简单,他们时间复杂度是一样的。

#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
#include<queue>
using namespace std;

typedef pair<int,int>PII;
const int N=210;

int n,m;
char g[N][N];
int dist[N][N];//来表示有没有搜过这个点

int bfs(PII start)//宽搜就需要用到队列
{
    queue<PII>q;
    q.push(start);
    memset(dist,-1,sizeof dist);
    dist[start.x][start.y];
    int dx[]={-1,0,1,0},dy[]={0,1,0,-1};//这可是一个小技巧哦!
    while(q.size())
    {
        auto t=q.front();//auto的两种用法:自动类型推断和返回值占位。
        q.pop();
        for(int i=0;i<4;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i];
            if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]!='#'&&dist[x][y]==0)//没有出界,也没有搜过
            {
                dist[x][y]=dist[t.x][t.y]+1;
                if(g[x][y]=='E')
                    return dist[x][y];
                q.push({x,y});//将当前点记录在队列里面
            }
        }
    }
    return -1;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        PII start;
        for(int i=0;i<n;i++)
        {
            cin>>g[i];
            for(int j=0;j<m;j++)
                if(g[i][j]=='S')
                start={i,j};
        }
        int res=bfs(start);
        if(res==-1)
            puts("oop!");
        else cout<<res<<endl;
    }

    return 0;
}

四、a^b(快速幂模板)背诵!

题目链接
求 a 的 b 次方对 p 取模的值。

输入格式
三个整数 a,b,p ,在同一行用空格隔开。

输出格式
输出一个整数,表示a^b mod p的值。

数据范围
0≤a,b≤109
1≤p≤109
输入样例:
3 2 7
输出样例:
2
快速幂

快速幂:反复平方法,a^b,把b转化为二进制

#include<iostream>

using namespace std;

typedef long long LL;

int qmi(int a,int b,int p)//这是快速幂的模板,要背诵
{
    int res=1;
    while(b)
    {
        if(b&1)//就是看b的最后一位是不是1
            res=(LL)res*a%p;
        a=(LL)a*a%p;
        b>>=1;
    }
    return res;
}
int main()
{
    int a,b,p;
    cin>>a>>b>>p;
    cout<<qmi(a,b,p)<<endl;//快速幂
    return 0;
}

五、 ISBN号码

每一本正式出版的图书都有一个 ISBN 号码与之对应。

ISBN 码包括 9 位数字、1 位识别码和 3 位分隔符,其规定格式如 x-xxx-xxxxx-x,其中符号 - 是分隔符(键盘上的减号),最后一位是识别码,例如 0-670-82162-4 就是一个标准的ISBN码。

ISBN 码的首位数字表示书籍的出版语言,例如 0 代表英语;第一个分隔符 - 之后的三位数字代表出版社,例如 670 代表维京出版社;第二个分隔之后的五位数字代表该书在出版社的编号;最后一位为识别码。

识别码的计算方法如下:

首位数字乘以 1 加上次位数字乘以 2……以此类推,用所得的结果 mod11,所得的余数即为识别码,如果余数为 10,则识别码为大写字母 X。

例如 ISBN 号码 0-670-82162-4 中的识别码 4 是这样得到的:对 067082162 这 9 个数字,从左至右,分别乘以 1,2,…,9,再求和,即 0×1+6×2+……+2×9=158,然后取 158mod11 的结果 4 作为识别码。

编写程序判断输入的 ISBN 号码中识别码是否正确,如果正确,则仅输出 Right;如果错误,则输出是正确的 ISBN 号码。

输入格式
输入只有一行,是一个字符序列,表示一本书的 ISBN 号码(保证输入符合 ISBN 号码的格式要求)。

输出格式
输出一行,假如输入的 ISBN 号码的识别码正确,那么输出 Right,否则,按照规定的格式,输出正确的 ISBN 号码(包括分隔符 -)。

输入样例1:
0-670-82162-4
输出样例1:
Right
输入样例2:
0-670-82162-0
输出样例2:
0-670-82162-4

这里get到一个新用法,看来还是基础知识不够牢固,在用string的时候,s.back()是指的是s的最后一个数字

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

int main()
{
    string str;
    cin>>str;
    int sum=0;
    for(int i=0,j=1;i+1<str.size();i++)
        if(str[i]!='-')
    {
        sum+=(str[i]-'0')*j;
        j++;
    }
    sum%=11;
    char c='X';
    if(sum<10)c='0'+sum;
    if(c==str.back())
        puts("Right");
        else {
            str.back()=c;
            cout<<c<<endl;
        }
    return 0;
}

六、数列

题目链接
给定一个正整数 k,把所有 k 的方幂及所有有限个互不相等的 k 的方幂之和构成一个递增的序列,例如,当 k=3 时,这个序列是:

1,3,4,9,10,12,13,…
该序列实际上就是:30,31,30+31,32,30+32,31+32,30+31+32,…
请你求出这个序列的第 N 项的值(用 10 进制数表示)。

例如,对于 k=3,N=100,正确答案应该是 981。

输入格式
输入文件只有 1 行,为 2 个正整数,用一个空格隔开:k,N。

输出格式
输出文件为计算结果,是一个正整数(在所有的测试数据中,结果均不超过 2.1×109)。(整数前不要有空格和其他符号)。

数据范围
3≤k≤15,
10≤N≤1000
输入样例:
3 100
输出样例:
981

考察二进制和映射
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

int power(int a,int b)
{
    int res=1;
    while(b--)
        res*=a;
    return res;
}
int main()
{
   int k,n;
   cin>>k>>n;
   int res=0;
   for(int i=0;i<10;i++)
       if(n>>i&1)//如果他的最后一位是1的话,我们就加上k的i次方
        //>>这个是除的意思
        res+=power(k,i);
       cout<<res<<endl;
    return 0;
}

六、陶陶摘苹果

陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 10 个苹果。

苹果成熟的时候,陶陶就会跑去摘苹果。

陶陶有个 30 厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。

现在已知 10 个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。

假设她碰到苹果,苹果就会掉下来。

输入格式
输入文件包括两行数据。

第一行包含 10 个 100 到 200 之间(包括 100 和 200)的整数(以厘米为单位)分别表示 10 个苹果到地面的高度,两个相邻的整数之间用一个空格隔开。

第二行只包括一个 100 到 120 之间(包含 100 和 120)的整数(以厘米为单位),表示陶陶把手伸直的时候能够达到的最大高度。

输出格式
输出文件包括一行,这一行只包含一个整数,表示陶陶能够摘到的苹果的数目。

输入样例:
100 200 150 140 129 134 167 198 200 111
110
输出样例:
5

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

int main()
{
    int apple[10];
    for(int i=0;i<10;i++)
        cin>>apple[i];
    int height;
    cin>>height;
    int res=0;
    for(int i=0;i<10;i++)
        if(apple[i]<=height+30)
        res++;
    cout<<res<<endl;
    return 0;
}


总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值