寒假每日一题(三)

 

 

文章目录

  • 前言
  • 一、翻硬币
    二、找硬币
          方法一:哈希表 O(n)
         方法二:双指针O(nlogn)
          小结
    三、十三号星期五
    四、平方矩阵II
                      方法一: 从对角线,向右延伸,向下延伸
                          方法二:一行一行来做(按行枚举)
                          方法三:最简单的一个
    五、棋盘挑战dfs
    六、货币系统(完全背包问题)
    七、阶乘
     总结

 


前言

寒假每日一题之疯狂补题!


 

一、翻硬币

小明正在玩一个“翻硬币”的游戏。

桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:**oo***oooo

如果同时翻转左边的两个硬币,则变为:oooo***oooo

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作。

输入格式

两行等长的字符串,分别表示初始状态和要达到的目标状态。

输出格式

一个整数,表示最小操作步数

数据范围

输入字符串的长度均不超过100。
数据保证答案一定有解。

输入样例1:

**********
o****o****

输出样例1:

5

输入样例2:

*o**o***o***
*o***o**o***

输出样例2:

1

出自于第四届蓝桥杯B组的题目。会想到八数码这个题,进行多少操作会变成目标状态。用bfs会超时。

先观察一下性质。①操作顺序对答案无影响。②每一个操作最多进行一次。

第一个硬币和最后一个硬币是否需要操作只需要看他本身,所有都是唯一确定的,不需要枚举。看似是个宽搜,实际上是个递推,最终方案也就只有一种。

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

const int N =310;
string a,b;

void turn (int i)
{
    if(a[i]=='*')
        a[i]='o';
    else a[i]='*';
}
int main()
{
   cin>>a>>b;
   int res=0;
   for(int i=0;i+1

 

二、找硬币

伊娃喜欢从整个宇宙中收集硬币。

有一天,她去了一家宇宙购物中心购物,结账时可以使用各种硬币付款。

但是,有一个特殊的付款要求:每张帐单,她只能使用恰好两个硬币来准确的支付消费金额。

给定她拥有的所有硬币的面额,请你帮她确定对于给定的金额,她是否可以找到两个硬币来支付。

输入格式

第一行包含两个整数 N 和 M,分别表示硬币数量以及需要支付的金额。

第二行包含 N 个整数,表示每个硬币的面额。

输出格式

输出一行,包含两个整数 V1,V2,表示所选的两个硬币的面额,使得 V1≤V2并且 V1+V2=M。

如果答案不唯一,则输出 V1 最小的解。

如果无解,则输出 No Solution

数据范围

1≤N≤10^5 
1≤M≤1000 

输入样例1:

8 15
1 2 8 7 2 4 11 15

输出样例1:

4 11

输入样例2:

7 14
1 8 7 2 4 11 15

输出样例2:

No Solution

很好的考察哈希表。此时数据范围是10^5,所以时间复杂度就是O(nlogn)[数据范围也很重要]

因为V1+V2=M,就是查找这个数的前面有没有一个数等于M-这个数,就是查找一个数是否存在,所以可以用哈希表来写,从小到大扫描的时候,每扫描一个数,就把它插在哈希表里面,对于当前数要看他前面有没有这个数,就在哈希表里面查一下。每个数遍历一次,O(n)。

方法一:哈希表 O(n)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#incluce//哈希表
using namespace std;

const int INF=10000;

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    unordered_set hash;//建立哈希表
    int  v1=INF,v2;//是寻找最小的答案即可
    for(int i=0;ib) swap(a,b);
            if(a

方法二:双指针O(nlogn)

先将整个数组从小到大排序,对于ai+aj<=M且j最大。如果前面的指针单调从前往后走,且后面的指针单调的从后往前走,那么这个时候就可以使用双指针的算法。双指针O(n),排序O(logn)

#include
#include
#include
#include
#include
#include
#include

using namespace std;

const int N=100010;
int n,m;
int w[i];

int main()
{
   scanf("%d%d",&n,&m);
   for(int i=0;im)j--;
       if(i

小结: 

但后面会发现,哈希表的运行速度是慢于双指针的,这是由于哈希表的STL比较慢,而且时间嘛,不能只看复杂度,还要看常数,双指针虽然时间复杂度高啊,但是他的常数比较小。

三、十三号星期五

十三号星期五真的很不常见吗?

每个月的十三号是星期五的频率是否比一周中的其他几天低?

请编写一个程序,计算 N年内每个月的 13 号是星期日,星期一,星期二,星期三,星期四,星期五和星期六的频率。

测试的时间段将会开始于 1900年 1月 1日,结束于 1900+N−1 年 12 月 31日。

一些有助于你解题的额外信息:

  1. 1900 年 1月 1 日是星期一。
  2. 在一年中,4 月、6月、9 月、11 月每个月 30 天,2 月平年28 天,闰年 29 天,其他月份每个月31天。
  3. 公历年份是 4 的倍数且不是 100 的倍数的年份为闰年,例如 1992 年是闰年,1990 年不是闰年。
  4. 公历年份是整百数并且是 400 的倍数的也是闰年,例如1700年,1800年,1900年,2100年不是闰年,2000年是闰年。

输入格式

共一行,包含一个整数 NN。

输出格式

共一行,包含七个整数,整数之间用一个空格隔开,依次表示星期六,星期日,星期一,星期二,星期三,星期四,星期五在十三号出现的次数。

数据范围

1≤N≤400

输入样例:

20

输出样例:

36 33 34 33 35 35 34

这一道星期几的题目有很多种做法,还有公式,基姆拉尔森公式,但公式做法不提倡,无法提高代码能力。就用个通用做法,就枚举天12w的计算量也还可以接受,枚举月会更快一些。枚举每月第一天距离1900年1月1号(起点)过了多少天(偏移量)。(偏移量+12)%7余几就可以了。有特殊的地方就是判断平年和闰年。

闰年     情况1:不能整除100但可以整除4;情况2:可以整除400;

在枚举月的时候可以开数组,很快的。

#include
#include
#include
#include
#include
#include
#include

using namespace std;

int moth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};//打表
int weekday[7];

int main()
{
    int n;
    cin>>n;
    for(int year=1900;year<1900+n;year++)//年
    {
        for(int i=1;i<=12;i++)//月
        {
            weekday[(days+12)%7]++;
            days+=month[i];
            if(i==2)
                {
                    if(year%100&&year%4==0||year%400==0)
                        days++;
                }
        }
    }
    for(int i=5,j=0;j<7;i=(i+1)%7,j++)
        cout<

 

四、平方矩阵II

输入整数N,输出一个N阶的二维数组。

数组的形式参照样例。

输入格式

输入包含多行,每行包含一个整数N。

当输入行为N=0时,表示输入结束,且该行无需作任何处理。

输出格式

对于每个输入整数N,输出一个满足要求的N阶二维数组。

每个数组占N行,每行包含N个用空格隔开的整数。

每个数组输出完毕后,输出一个空行。

数据范围

0≤N≤100

输入样例:

1
2
3
4
5
0

输出样例:

1

1 2
2 1

1 2 3
2 1 2
3 2 1

1 2 3 4
2 1 2 3
3 2 1 2
4 3 2 1

1 2 3 4 5
2 1 2 3 4
3 2 1 2 3
4 3 2 1 2
5 4 3 2 1

不同的看法,不同的解法。有的时候写不出来,可以换一个看法。

方法一: 从对角线,向右延伸,向下延伸

#include
#include
#include
#include
#include
#include
#include

using namespace std;

const int N=110;

int main()
{
    int n;
    while(cin>>n,n)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=i,k=1;j<=n;j++,k++)//great!这样子来表示好巧
            {
                a[i][j]=k;
                a[j][i]=k;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                cout<

方法二:一行一行来做(按行枚举)

#include
#include
#include
#include
#include
#include
#include

using namespace std;


int main()
{
    int n;
    while(cin>>n,n)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j>=1;j--)
            cout<

这个方法都不用数组了!

方法三:最简单的一个

找每一个数跟他的行的关系:|i-j|+1

#include
#include
#include
#include
#include
#include
#include

using namespace std;


int main()
{
    int n;
    while(cin>>n,n)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            cout<

五、棋盘挑战dfs

给定一个N×N 的棋盘,请你在上面放置 N 个棋子,要求满足:

  • 每行每列都恰好有一个棋子
  • 每条对角线上都最多只能有一个棋子
    1   2   3   4   5   6
  -------------------------
1 |   | O |   |   |   |   |
  -------------------------
2 |   |   |   | O |   |   |
  -------------------------
3 |   |   |   |   |   | O |
  -------------------------
4 | O |   |   |   |   |   |
  -------------------------
5 |   |   | O |   |   |   |
  -------------------------
6 |   |   |   |   | O |   |
  -------------------------

上图给出了当 N=6 时的一种解决方案,该方案可用序列 2 4 6 1 3 5 来描述,该序列按顺序给出了从第一行到第六行,每一行摆放的棋子所在的列的位置。

请你编写一个程序,给定一个 N×N 的棋盘以及 N 个棋子,请你找出所有满足上述条件的棋子放置方案。

输入格式

共一行,一个整数 N。

输出格式

共四行,前三行每行输出一个整数序列,用来描述一种可行放置方案,序列中的第 ii 个数表示第 ii 行的棋子应该摆放的列的位置。

这三行描述的方案应该是整数序列字典序排在第一、第二、第三的方案。//你要按照正常人的做法做就可以了哈哈哈哈

第四行输出一个整数,表示可行放置方案的总数。

数据范围

6≤N≤13//一般都是13

输入样例:

6

输出样例:

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

十分经典的暴搜问题,叫八皇后(n皇后)问题。超超超超经典哦!为了理解,自己可以画一下递归搜索树。 按行来枚举 ,一般有对称方法。

dfs天然保证字典序。还要注意一点,恢复现场

#include
#include
#include
#include
#include
#include
#include

using namespace std;

const int N=15;
int n;
bool col[N],dg[N*2],udg[N*2];//后面两个是对角线和反对角线
int path[N],ans;

void dfs(int x)
{
    if(x>n)
    {
        ans++;
        if(ans<=3)//输出前三个方案
        {
            for(int i=1;i<=n;i++)
            cout<>n;
    dfs(1);
    cout<

六、货币系统(完全背包问题

给定 V 种货币(单位:元),每种货币使用的次数不限。

不同种类的货币,面值可能是相同的。

现在,要你用这 V种货币凑出 N元钱,请问共有多少种不同的凑法。

输入格式

第一行包含两个整数 V 和 N。

接下来的若干行,将一共输出 VV 个整数,每个整数表示一种货币的面值。

输出格式

输出一个整数,表示所求总方案数。

数据范围

1≤V≤25
1≤N≤10000
答案保证在long long范围内。

输入样例:

3 10
1 2 5

输出样例:

10

完全背包问题

dp分析法,从两个地方考虑,①状态表示②状态计算,dp的方法比暴搜好呢是因为dp呢他是可以解决一类问题,而暴搜呢就是只能解决一个问题,暴搜dfs的时间复杂度就是指数级别的。背包定义是两维,表示的是哪一个集合。dp有点跟数学归纳法类似。

①分为集合(所有满足条件的集合,从1~i中选,且总钱数为j的方案的集合)还有属性(就是这个题问的是什么,本题就是数量)

②对应的是集合划分,分解为若干个子集,然后各个击破,最后一个不同点。

f(i,j)的意思是所有从1~i中选,且花钱是j的方案的集合。f(i,j)=f(i-1,j)+f(i,j-vi),然后再考虑优化,先优化时间,在优化空间

二维空间写法

#include
#include
#include
#include
#include
#include
#include

using namespace std;

typedef long long LL;
const int N=30,M=10010;

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

int main()
{
  cin>>n>>m;
  f[0][0]=1;
  for(int i=1;i<=n;i++)
  {
      int v;
      cin>>v;
      for(int j=0;j<=m;j++)
      {
          f[i][j]=f[i-1][j];
          if(j>=v)
            f[i][j]+=f[i][j-v];
      }
  }
  cout<

空间优化,一维空间写法

#include
#include
#include
#include
#include
#include
#include

using namespace std;

typedef long long LL;
const int N=30,M=10010;

int n,m;
LL f[M];

int main()
{
  cin>>n>>m;
  f[0]=1;
  for(int i=1;i<=n;i++)
  {
      int v;
      cin>>v;
      for(int j=v;j<=m;j++)
      {
       
          if(j>=v)
            f[j]+=f[j-v];
            /*f[i][j]=f[i-1][j]+f[i][j-v]*/
      }
  }
  cout<

七、阶乘

NN 的阶乘(记作 N!)是指从 1 到 N(包括 1 和 N)的所有整数的乘积。

阶乘运算的结果往往都非常的大。

现在,给定数字 N,请你求出 N!的最右边的非零数字是多少。

例如 5!=1×2×3×4×5=120,所以 5! 的最右边的非零数字是 2。

输入格式

共一行,包含一个整数 N。

输出格式

输出一个整数,表示 N! 的最右边的非零数字。

数据范围

1≤N≤1000

输入样例:

7

输出样例:

4

一看到这一道题我就想到了N^N的最高位,但这一题可以发现规律,末位数就是1和偶数(除了0),去0就是除以10^k,mod 0就是所求的数字,(分解质因数的思想)分解一下2,5的那个系数的最小值就是k

O(nlogn)

#include
#include
#include
#include
#include
#include
#include

using namespace std;

int main()
{
 int n;
 cin>>n;
 int res=1,d2=0,d5=0;//d2和d5是2和5的数量
 for(int i=1;i<=n;i++)
 {
     int x=i;
     while(x%2==0)x/=2,d2++;
     while(x%5==0)x/=5,d5++;
     res=res*x/10;
 }
 int k=min(d2,d5);
 for(int i=0;i

 

 

 


总结

还有25道题,干!

你可能感兴趣的:(2021每日一题)