整理 1

整理一下牛客网上的我觉得值得mark的题目,代码都是cpp.

1.求最小公倍数

本题实际上是求最大公约数。
如何求最大公约数? 辗转相除法。
m a x d i f ( a , b ) = { a b = 0 m a x d i f ( b , a % b ) o t h e r maxdif(a,b)= \begin{cases} a & b = 0 \\ maxdif(b,a\%b) & other \end{cases} maxdif(a,b)={amaxdif(b,a%b)b=0other
正整数A和正整数B 的最小公倍数是指 能被A和B整除的最小的正整数值,设计一个算法,求输入A和B的最小公倍数。

#include<iostream>
using namespace std;

int max_dif(int a, int b) //辗转相除法
{
    while(b != 0)
    {
        int tmp = a % b;
        a = b;
        b = tmp;
    }
    return a;
}
int main()
{
    int a,b;
    while(cin >> a >> b)
    {
        int maxdif = max_dif(a,b);
       // cout << maxdif;
        int result = (a / maxdif) * maxdif * (b / maxdif); 
        cout << result << endl;
    }
}

2.求一个数的立方根

设输入数为a
1.二分法,在0 - a直接二分查找即可,但是效率太慢。code就不写了。。。
2.牛顿迭代法 (具体证明不要问我,我也不会 ﹁ ﹁ )
f ( x ) = x 3 − a f(x) = x^3 - a f(x)=x3a
f ( x ) = f ( x 0 ) + ( x − x 0 ) f ′ ( x 0 ) = 0 f(x) = f(x_0) + (x - x_0)f'(x_0) = 0 f(x)=f(x0)+(xx0)f(x0)=0
x − x 0 = − f ( x 0 ) f ′ ( x 0 ) x - x_0 = \frac{-f(x_0)}{f'(x_0)} xx0=f(x0)f(x0)
x = x 0 − f ( x 0 ) / f ′ ( x 0 ) x = x_0 - f(x_0)/f'(x_0) x=x0f(x0)/f(x0)
x k + 1 = x k − f ( x k ) / f ′ ( x k ) x_{k + 1} = x_k -f(x_k)/f'(x_k) xk+1=xkf(xk)/f(xk)
x k x_k xk x k + 1 x_{k+1} xk+1接近时, x k x_k xk即为所求的立方根

#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
/*
fx = x^3 - a = 0
fx = fx_0 + (x - x_0)^f'(x_0) = 0
x - x_0 = - fx_0 / f'(x_0)
x = x_0 - fx_0 / f'(x_0)
*/
double sqrt_3(double a);
int main()
{
    double n;
    while(cin >> n)
    {
        double result = sqrt_3(n);
        cout << fixed << setprecision(1) << result << endl;
        
    }
    return 0;
}


double sqrt_3(double a)
{
    double n = 1;
    double n1 = n - (n * n * n - a)/ (3 * n * n);
    while(abs(n - n1) > 1e-5)
    {
        n = n1;
        n1 = n - (n * n * n - a)/ (3 * n * n);
    }
    return n1;

3.Redraiment的走法

Redraiment是走梅花桩的高手。Redraiment总是起点不限,从前到后,往高的桩子走,但走的步数最多,不知道为什么?你能替Redraiment研究他最多走的步数吗?

实际上就是求最长的严格递增子序列。
利用动态规划的思想可做。
d p [ j ] = m a x ( d [ i ] ) + 1 0 < = i < j dp[j] = max(d[i]) + 1 \qquad 0<= i < j dp[j]=max(d[i])+10<=i<j

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;

int main()
{
  int n;
  while( cin >> n)
  {
      vector<int> arr;
      for(int i = 0 ; i < n;i++)
      {
          int tmp;
          cin >> tmp;
          arr.push_back(tmp);
      }
      vector<int> result;
      result.resize(n);//result[i]代表末尾为第i位的最长递增子序列长度
      
      int max_step = 0;
      for(int i = 0;i < n;i++)
      {
          result[i] = 1;
          for(int j = 0; j < i;j++)
          {
              if(arr[j] < arr[i])
                  result[i] = max(result[i],result[j] + 1);       
          }
          max_step = max(max_step,result[i]);
          
      }
      cout<<max_step<<endl;
  }
}

4.自守数

自守数是指一个数的平方的尾数等于该数自身的自然数。例如:25^2 = 625,76^2 = 5776,9376^2 = 87909376。请求出n以内的自守数的个数。
看了牛客网上90%的题解都是用长整形数据存储,很无语。。。
N-自守数


#include <stdio.h>

int main()
{
   int M, N, K, L, mask;

   scanf("%d", &M);
   for(int i = 0; i < M; i++)
   {
       scanf("%d", &K);
       for(N = 1; N < 10; N++)
       {
           L = N * K * K;

           /* compare the lowest digits one by one */
           for(mask = 1; mask <= K; mask *= 10)
               if(L / mask % 10 != K / mask % 10)
                   break;

           if(mask > K)  /* all passed! */
           {
               printf("%d %d\n", N, L);
               break;
           }
       }
       if(N == 10)  /* No results */
           printf("No\n");
   }

   return 0;
}

5.埃及分数

分子为1的分数称为埃及分数。现输入一个真分数(分子比分母小的分数,叫做真分数),请将该分数分解为埃及分数。如:8/11 = 1/2+1/5+1/55+1/110。
埃及分数存在多种解法,这里只能保证生成的分数组合最短。
贪心算法

/*
a / b  ( a < b)
b = a * c + d   d:[0,c)
b / a = c + d/c < c + 1
a / b > 1 / (c + 1) (最大埃及分数为1/c + 1)
a / b - 1 / (c + 1) = a(c + 1) - b/ b ( c + 1)
*/
#include<iostream>

#include<vector>
using namespace std;

int max_div(int a, int b)
{
   int tmp = 0;
   while(b != 0)
   {
       tmp = a % b;
       a = b;
       b = tmp;
   }
   return a;
}
int main()
{
   int a,b;
   char ch;
   while(cin >> a >> ch >> b)
   {
       //cout << a << b << endl;
       int count = 0;
      do{
          int e = b / a + 1;
          if(count != 0)
              cout <<"+";
          cout << "1/" << e;
          a = a * e - b;
          b = b * e;            
          int maxd = max_div(a,b);
          if(maxd > 1)
          {
              b = b / maxd;
              a = a / maxd;
          } //不影响算法正确性,但是能保证生成的埃及分数序列最小。
          count++;
      } while(a > 1);
       cout <<"+1/" << b << endl;
   }
   return 0;
}

6. 24点算法

问题描述:给出4个1-10的数字,通过加减乘除,得到数字为24就算胜利
输入:
4个1-10的数字。[数字允许重复,但每个数字仅允许使用一次,测试用例保证无异常数字]
输出:
true or false
经典回溯法的问题。
先从4个数中任取两个数计算结果,更新在某个数据上
然后以此类推,最后只剩一个数的时候判断是否 = 24;
详细的思路讲解去leetcode上看。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

bool find24points(vector<double> c);

int main()
{
    vector<double> nums;
    nums.resize(4);
    while(cin >> nums[0] >> nums[1] >> nums[2] >> nums[3])
    {
        if(find24points(nums))
            cout<<"true"<<endl;
        else cout<<"false"<<endl;
    }
}

bool find24points(vector<double> cur_num)
{
    int len = cur_num.size();
    if(len == 1)
    {
        return abs(cur_num[0] - 24) <= 1e-5 ?true:false;
    }
    else 
    {
        for(int i = 0;i < len;i++)
        {
            for(int j = 0;j < len;j++)
            {
                if(j == i)
                    continue;
                vector<double> new_arr;
                for(int k = 0; k < len ;k++)
                    if(k != i && k != j)
                    {
                        new_arr.push_back(cur_num[k]);
                    }
                    else continue;
                double t1 = cur_num[i] + cur_num[j];
                new_arr.push_back(t1);
                if(find24points(new_arr))
                     return true;
                new_arr.pop_back();
                
                double t2 = cur_num[i] - cur_num[j];
                new_arr.push_back(t2);
                if(find24points(new_arr))
                     return true;
                new_arr.pop_back();
                
                double t3 = cur_num[i] * cur_num[j];
                new_arr.push_back(t3);
                if(find24points(new_arr))
                     return true;
                new_arr.pop_back();
                
                if(cur_num[j] == 0)
                    continue;
                double t4 = cur_num[i] / cur_num[j];
                new_arr.push_back(t4);
                if(find24points(new_arr))
                     return true;
                new_arr.pop_back();
                
            }
        }
        return false;
    }
}

7. 查找输入二进制中整数1的个数

#include
using namespace std;
// 需要考虑负数,所以不能讲输入数字右移。
int main()
{
int n;
while(cin >> n)
{
int count = 0;
int flag = 1;
while(flag != 0)
{
//cout << flag << " " << count << endl;
if(flag & n) // 注意& ,<<的优先级,他们优先级是最低的
count++;
flag = flag << 1;
}
cout<<count<<endl;
}
}

或者使用
//把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0,那么一个整数的二进制表示中有
多少个1,就可以进行多少次这样的操作。
while(n != 0
{
		n = ( n - 1) & n;
		count ++;
}
cout << count

8.放苹果问题

题目描述

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
0 <= m <= 10 1 <= n <= 10
状态转移方程
C ( m , n ) = { 1 m = 0 ∣ ∣ n = 1 C ( m , n − 1 ) + C ( m − n , n ) m > = n C(m,n) = \begin{cases}1 & m = 0 || n = 1\\C(m,n - 1) + C(m - n,n) & m >= n \\ \end{cases} C(m,n)={1C(m,n1)+C(mn,n)m=0n=1m>=n
大概解释一下就是苹果放盘子,要么存在空盘,要么不存在空盘。

#include<iostream>
#include<vector>
using namespace std;
// m [0,10] n[1,10]
int main()
{
    int m,n;
    while(cin >> m >> n)
    {
        vector<vector<int>> dp;
        dp.resize(m + 1);
        for(int i = 0;i < m + 1;i++)
            dp[i].resize(n + 1);
        
        for(int i = 0; i < m + 1;i++)
            dp[i][1] = 1;
        
        for(int i = 1; i < n + 1;i++)
        {
            dp[1][i] = 1;
            dp[0][i] = 1;
        }
        for(int i = 2; i < m + 1 ;i++)
        {
            for(int j = 2; j < n + 1;j++)
            {
                if(i >= j)
                    dp[i][j] = dp[i][j - 1] + dp[i - j][j];
                else dp[i][j] = dp[i][j - 1];
            }
        }
        cout << dp[m][n] << endl;
    }
}

9.查找字符串中第一个只出现一次的字符。

遍历字符串,对字符串中的每一个字符都同时进行从左边查找以及右边开始查找,若下标一样则返回当前下标为出现的第一个字符。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
// rfind 从字符串末尾开始查找
int main()
{
    string s;
    while(getline(cin , s))
    {
        string new_str;
        vector<int> char_set(26,0);
        int i = 0;
        for(; i < s.length() ; i++)
        {
            if(s.find(s[i]) != s.rfind(s[i])) //左向查找and右向查找。
                continue;
            else {
                break;
            }
        }
        if(i == s.length())
            cout << - 1 << endl;
        else cout << s[i] << endl;
    }
    return 0;
}

10.合唱队

计算最少出列多少位同学,使得剩下的同学排成合唱队形

说明:

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1<=i<=K)使得T1<T2<…<Ti-1Ti+1>…>TK。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
本题目实质上还是求最长递增子序列的问题。
需要计算每位同学在以他为队尾的时候最长递增子序列的长度。

#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;

int main()
{
    int n;
    while(cin >> n)
    {
        vector<int> arr;
        for(int i = 0; i < n;i++)
        {
            int tmp;
            cin >> tmp;
            arr.push_back(tmp);
        }
        
        vector<int> up_arr;
        up_arr.resize(n);
        
        for(int i = 0; i < n ;i++)
        {
            up_arr[i] = 1;
            for(int j = 0; j < i;j++)
            {
                if(arr[j] < arr[i])
                    up_arr[i] = max(up_arr[i],up_arr[j] + 1);
            }
        }
        
        vector<int>  down_arr;
        down_arr.resize(n);
        for(int i = n - 1; i >= 0;i--)
        {
            down_arr[i] = 1;
            for(int j = n - 1;j > i;j--)
                if(arr[j] < arr[i])
                    down_arr[i] = max(down_arr[i],down_arr[j] + 1);
        }
        int max_K = 0;
        for(int i = 0 ; i < n;i++)
        {
            up_arr[i] += down_arr[i] - 1;
            max_K = max(up_arr[i],max_K);
        }
        cout<< n - max_K <<endl;
        
        
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值