算法训练(更新中。。。)

 ALGO-1007 印章(DP)

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
  一行两个正整数n和m
输出格式
  一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
方法一:
#include<iostream>
#include<iomanip>
#include<math.h>
using namespace std;
double dp[22];//一维数组,dp[i]:买i张印章,凑齐n种印章的概率 
/*为什么用一维数组?
因为在此解法所用的递推公式中,
买m张印章,凑齐n种印章的概率只与买m张印章,凑齐n-1种,n-2种...1种 的概率有关 ,
与买x(x!=m)张印章凑齐y(y>=1)种印章的概率无关。
这是一个具体的问题,就是说明了买了m张,求的是里面刚好有n,这个事件的概率*/
long long c_fun(int n, int m)//求C(n,m),注意n为下标,m为上标
{
    long i;
    long long ans_n = 1, ans_m = 1, ans_nm = 1;
    for (i = 2; i <= n; i++)
        ans_n = ans_n * i;
    for (i = 2; i <= m; i++)
        ans_m = ans_m * i;
    for (i = 2; i <=(n-m) ; i++)
        ans_nm = ans_nm * i;
    return ans_n / (ans_m * ans_nm);
}
double fun(int n, int m)//dp[n],求印章问题,待求事件的概率
{
    int i, j;
    dp[1] = 1;//对于凑齐1种,只要买了概率的发生一定是1。
    for (i = 2; i <= n; i++)//这个循环求凑齐2、3、...n种的概率,后面一个数字发生的概率需要前一个发生的概率
    {
        double t = 0;        //i代表凑齐的种数,j的取值为1--m,
        for (j = 1; j < i; j++)//当j>i时,意味着买的印章数<凑齐的印章种数,此事件不可能发生
            t += c_fun(i, j) * pow(j, m) * dp[j];//求逆事件的概率
        dp[i] = 1 - t / pow(i, m);//总概率为1,1减去逆事件的概率为待求事件概率;
        //看到这一步,其实可以想到另一种方法直接求待求事件的概率;
    }
    return dp[n];
}
int main()
{
    int n, m;//n=凑齐的种数,m=买的印章数;
    cin >> n >> m;
    dp[n] = fun(n, m);
    cout << fixed<<setprecision(4)<< dp[n] << endl;
    return 0;
}

做这道题发现蓝桥杯系统有bug诶,就算是通过了所有的测试,代码的逻辑还是有问题的。不过这也是很正常的是,测试代码是否正确他只看输入输出是否正确,只要通过那几组测试,都没啥大问题。

参考文章: 蓝桥杯算法训练 印章_!YI的博客-CSDN博客

方法二:

#include<iostream>
#include<math.h>
#include<iomanip>
using namespace std;
double dp[25][25] ;
int main()
{
    int n, m;
    cin >> n >> m;
    double p=1.0/n;//每种出现的概率,设一个double型的p也为方面了后面的运算,使运算结果为double型
    for (int i = 1; i <= m; i++)
    {
        dp[i][1] = pow(p, i - 1);//dp[i][1]=p^(n-1);
        for (int j = 2; j <= i; j++)
            dp[i][j] = dp[i-1][j - 1] * ((n-j+1)*p) + dp[i - 1][j]*(j*p);
        //((n-j+1)/n)  典型错误,计算结果为0
    /*我们买的第 i 张,有两种状态,一:跟前面买的 i - 1 张中有重复的 二:跟前面买的 i - 1 张中没有重复的
        买第i张如果有重复的着事件发生的概率为dp[i - 1][j]*(j*p);
        如果没有重复的则抽到第i与前面的种数有重复则事件发生的概率为dp[i-1][j - 1] * ((n-j+1)*p)
        注意是dp[i-1][j - 1] * ((n-j+1)*p)不是dp[i][j - 1] * ((n-j+1)*p),抽一张新的种类i、j都要+1,
        所以对于dp[i][j]的概率,是上面两种可能概率相加*/
    }
    cout << fixed<<setprecision(4)<<dp[m][n] << endl;
    return 0;
}

其实这个方法可以设数组为一维数组,还有想说的是for循环yyds!!!

贴一下下面分享的博主写的,他的for里面加了if语句,这个想法很好诶!

#include <iostream>
#include <cmath>
using namespace std;
double dp[25][25], p;
int main()
{
    
    //记住是小数啊,要*1.0进行类型转换的
    int n, m;
    cin >> n >> m;
    p = 1.0 / n; //每种出现的概率
    
    for ( int i = 1; i <= m; ++i ) {
        for ( int j = 1; j <= n; ++j ) {
            if ( i <  j ) dp[i][j] = 0;
            if ( j == 1 ) {
                dp[i][j] = pow (p, i-1);  //p^(i-1)
            }
            else {
                dp[i][j] = dp[i-1][j] * (j*1.0/n) + dp[i-1][j-1] * ((n-j+1)*1.0/n);
            }
        }
    }
//    cout << "dp\n";
//        for ( int i = 1; i <= m; ++i ) {
//        for ( int j = 1; j <= n; ++j ) {
//            printf("%.2lf  ",dp[i][j]);
//        }
//        cout << endl;
//    }
    
    printf("%.4lf  ",dp[m][n]);
    return 0;
}

总结一下这个题考的就是数学,求概率,可惜我概率论学的撇啊!

参考文章: 蓝桥杯 试题 算法训练 印章_okok__TXF的博客-CSDN博客

ALGO-1006 拿金币(DP)

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。
输入格式
  第一行输入一个正整数n。
  以下n行描述该方格。金币数保证是不超过1000的正整数。
输出格式
  最多能拿金币数量。
样例输入
3
1 3 3
2 2 2
3 1 2
样例输出
11
数据规模和约定
  n<=1000
#include<iostream>
using namespace std;
int dp[1002][1002] = { 0 };
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)//给一个位置赋值,值代表这个位置金币的数量
        for (int j = 1; j <= n; j++)
            cin >> dp[i][j];
    for (int i = 1; i <= n; i++)//遍历所有的位置,每一个位置的值都+=max(左边或者上边)
        for (int j = 1; j <= n; j++)//在草稿纸上面画一哈,其实就可以确定走的路径,
        {                           //对于dp[n][n]的值来说,它一定的走的所有路径中所加值最大的那条路的值
            if (dp[i - 1][j] > dp[i][j - 1])
                dp[i][j] += dp[i - 1][j];
            else
                dp[i][j] += dp[i][j - 1];
        }
    cout << dp[n][n] << endl;//分析可知要使捡的金币最多一定是走到了(n,n)这个位置的。
    return 0;
}

参考文章: LanQiao-ALGO-1006 拿金币(动态规划) (类似 LeetCode 62.不同路径)_Mr.xiao的博客-CSDN博客

感谢大佬的文章,这个题其实考了图的知识。

ALGO-1005 数字游戏(搜索)

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  给定一个1~N的排列a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少1,最终只剩一个数字。
  例如:
  3 1 2 4
  4 3 6
  7 9
  16
  现在如果知道N和最后得到的数字sum,请求出最初序列a[i],为1~N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。
输入格式
  第1行为两个正整数n,sum
输出格式
  一个1~N的一个排列
样例输入
4 16
样例输出
3 1 2 4
数据规模和约定
  0<n<=10
方法一:STL函数 next_permutation
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int c_fun(int a, int b)//求C(a,b)
{
    int ans_a = 1, ans_b = 1, ans_ab = 1;
    for (int i = 2; i <= a; i++)
        ans_a *= i;
    for (int i = 2; i <= b; i++)
        ans_b *= i;
    for (int i = 2; i <= a - b; i++)
        ans_ab *= i;
    return ans_a / (ans_b * ans_ab);
}
int main()
{
    int n, sum;
    cin >> n >> sum;
    vector<int> lst;
    for (int i = 1; i <= n; i++)
        lst.push_back(i);
    do {
        int temp = 0;
        for (int i = 0; i < n; i++)
            temp += lst[i] * c_fun(n - 1, i);//规律,正推推出来的 temp = a[0]*C(0,n-1) + a[1]*C(1,n-1) + ... +a[n-1]*C(n-1,n-1);
        if (sum == temp)//在if里面写输出语句保证了输出结果存在则有输出,不存在无输出
        {
            for (vector<int>::iterator it = lst.begin(); it != lst.end(); it++)
                cout << *it << " ";
            break;
        }
    } while (next_permutation(lst.begin(), lst.end()));
    return 0;
}

参考文章: 蓝桥杯 数字游戏 C++_m0_56318237的博客-CSDN博客     

方法二:dfs

dfs方法不是很好,下次来补充

ALGO-1004 无聊的逗

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中。不过他想到了一个游戏来使他更无聊。他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的情况下长度最长是多少。
输入格式
  第一行一个数n,表示n个棍子。第二行n个数,每个数表示一根棍子的长度。
输出格式
  一个数,最大的长度。
样例输入
4
1 2 3 1
样例输出
3
数据规模和约定
  n<=15
 方法一:dfs
#include<iostream>
#include<vector>
#include<numeric>
#include<algorithm>
using namespace std;
int maxLen = 0;
void dfs (vector<int>dp, int n, int add, int compare = 0, int scan = 0)
{
    if (scan >= n) return;//递归结束条件,遍历完所有可能的结果
    //如果add==compare,且新计算得到的compare大于原来的maxLen,则更新maxLen;
    if (add == compare && maxLen < compare) maxLen = compare;
    //如果选择scan这个位置的木棍来组成compare的值
    dfs(dp, n, add - dp[scan], compare + dp[scan], scan + 1);
    //如果不选则scan这个位置的木棍来组成compare的值
    dfs(dp, n, add, compare, scan + 1);
}
int main()
{
    int n, a = 0;
    cin >> n;
    vector<int>dp(n);
    for (int i = 0; i < n; i++)
        cin >> dp[i];
    int sum = accumulate(dp.begin(), dp.end(), 0);//计算所有木棍的总长度
    sort(dp.begin(), dp.end(), greater<int>());//逆序排
/*逆序排的好处?dp[0]存的为最大的值,方便后序处理。*/
    /*此题的输入的棍子长度总数大概有三种情况,
    1.max==sum/2;
    2.max>sum/2,删除最大值后再排,也就是扫描的位置可以直接从1开始。
    3.sum%2!=0,删除最小奇数
    4.sum为偶数;  如果数能被分为两堆一样长,则这个数一定为偶数,
    所以我们要把范围局限为偶数,偶数都没法,这没法分了*/
    if ((double)dp[0] == sum / 2.0)//1.
    {
        cout << dp[0];
        return 0;
    }
    //if 最大长度大于sum/2,则删除最大长度木棍;
    if (dp[0] > sum / 2)//2.
        dfs(dp, n, sum - dp[0], a, 1);
    else if (sum % 2 != 0)//3.
    {
        for (int i = n - 1; i >= 0; i--)
            if (dp[i] % 2 != 0)
            {
                sum = sum - dp[i];
                for (int j = i; j < n - 1; j++)
                {
                    dp[j] = dp[j + 1];
                }
                n = n - 1;

                break;
            }
        dfs(dp, n, sum, a, 0);
    } else
    {
        dfs(dp, n, sum, a, 0);//4.
    }
    cout << maxLen << endl;
    return 0;
}

参考:蓝桥杯 试题 算法训练 无聊的逗 C++ 详解_Lyz_ID的博客-CSDN博客

方法二.01背包

链接放下面,属于是没看懂,等今后再来看。

参考文章:LanQiao-ALGO-1004 无聊的逗 (动态规划: 0-1背包问题) -- (LeetCode 416. 分割等和子集 变种题)_Mr.xiao的博客-CSDN博客

ALGO-1003 礼物

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  JiaoShou在爱琳大陆的旅行完毕,即将回家,为了纪念这次旅行,他决定带回一些礼物给好朋友。
  在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子。
  这些石子很漂亮,JiaoShou决定以此为礼物。
  但是这N个石子被施加了一种特殊的魔法。
  如果要取走石子,必须按照以下的规则去取。
  每次必须取连续的2*K个石子,并且满足前K个石子的重量和小于等于S,后K个石子的重量和小于等于S。
  由于时间紧迫,Jiaoshou只能取一次。
  现在JiaoShou找到了聪明的你,问他最多可以带走多少个石子。
输入格式
  第一行两个整数N、S。
  第二行N个整数,用空格隔开,表示每个石子的重量。
输出格式
  第一行输出一个数表示JiaoShou最多能取走多少个石子。
样列输入
  8 3
  1 1 1 1 1 1 1 1
样列输出
  6
样列解释
  任意选择连续的6个1即可。
数据规模和约定
  对于20%的数据:N<=1000
  对于70%的数据:N<=100,000
  对于100%的数据:N<=1000,000,S<=10^12,每个石子的重量小于等于10^9,且非负
方法:二分法
#include<iostream>
using namespace std;
int n;
long long s;
const int M = 1e6 + 10;//包括了所有数组取值大小
int weight[M];//每一个石头的重量
long long suffix[M];//前i个石头的重量和

bool check(int mid) {//mid的值是数组的下标,也表示了输出石头的个数
    for (int i = mid; i <= n - mid; i++) {
        if (suffix[i] - suffix[i - mid] <= s && suffix[i + mid] - suffix[i] <= s)
            //if 语句里面保证了前mid 个数小于s,后mid个数小于s ;
            return true;
    }
    return false;
}

int main() {
    cin >> n >> s;
    suffix[0] = 0;//给0位置赋值 0

    for (int i = 1; i <= n; i++) {
        cin >> weight[i];
        suffix[i] = suffix[i - 1] + weight[i];
    }

    int l, r, mid;
    l = 1;
    r = n;

    while (l <= r) {//二分法
        mid = (l + r) / 2;
        if (check(mid)) l = mid+1;
        else r = mid - 1;
    }

    cout << 2 * (l-1) << endl;//为什么l要-1呢?因为运行到l=r时肯定满足条件,
    //此时循环继续l的值变为l+1>r,调出循环,此时没有进行cheak()运算。

    return 0;
}

ALGO-1002 跳马

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  一个8×8的棋盘上有一个马初始位置为(a,b),他想跳到(c,d),问是否可以?如果可以,最少要跳几步?
输入格式
  一行四个数字a,b,c,d。
输出格式
  如果跳不到,输出-1;否则输出最少跳到的步数。
样例输入
1 1 2 3
样例输出
1
数据规模和约定
  0<a,b,c,d≤8且都是整数。
 方法:BFS
#include<iostream>
#include<queue>
using namespace std;
bool vis[9][9] = { 0 };//建立9*9的数组,其中(1-8)*(1-8)代表棋盘
//dx和dy一一对应,表示马走一步代表的走法,共八种(0,0)不算
int dx[9] = { 0,-2,-2,-1,-1,1,1,2,2 }; 
int dy[9] = { 0,-1,1,-2,2,-2,2,-1,1 };
queue<node> q;//建立队列q,用BFS肯定需要队列辅助呀

/*建立一个节点,节点代表每一个点的坐标(x,y)
以及从初始位置走到此点需要走几步*/
struct node
{
    int x, y;
    int step;
    node() :x(), y(), step() {};
    node(int a, int b, int c) :x(a), y(b), step(c) {};
};

int BFS_search(int a, int b, int c, int d)
{
    if (a == c && b == d)//如果目标点就是起始点
        return 0;
    vis[a][b] = true;//代表此点已经走过,修改值为true
    q.push(node(a, b, 0));//进入队列
    while (!q.empty())
    {
        node t = q.front();//头指针的值赋值给t;
        q.pop();//出队
        for (int i = 1; i <= 8; i++)//广度遍历关键操作,把每一种走法走一遍
        {
            int cur_x = t.x + dx[i];
            int cur_y = t.y + dy[i];
            if (cur_x == c && cur_y == d)
            {
                return t.step + 1;
            }
            else if(cur_x>=1&&cur_x<=8&&cur_y>=1&&cur_y<=8&&!vis[cur_x][cur_y])
            {
                q.push(node(cur_x, cur_y, t.step + 1));
                vis[cur_x][cur_y] = true;
            }
        }
    }
    return -1;
}
int main()
{
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    cout << BFS_search(a, b, c, d);
    return 0;
}

参考:试题 算法训练 跳马_Solar_angel的博客-CSDN博客

 

ALGO-1001 kAc给糖果你吃(贪心)

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  kAc有n堆糖果,每堆有A[i]个。
  kAc说你只能拿m次糖果,聪明的你当然想要拿最多的糖果来吃啦啦啦~
  //第二天,kAc问你还想吃糖果么?(嘿嘿嘿)说着眼角路出奇怪的微笑...
输入格式
  第一行两个数字n和m,第二行有n个数字A[i]。
输出格式
  输出一行表示最多能拿几个糖果。
样例输入
2 2
1 2
样例输出
3
数据规模和约定
  0<n≤1000
  其余数字都是不超过1,000,000,000的非负整数。
 
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int main()
{
    int n, m;
    long long sum=0;
    cin >> n >> m;
    vector<long long> A(n);
    for (int i = 0; i < A.size(); i++)
         cin>> A[i];
    sort(A.begin(), A.end(), greater<long long>());//从大到小排
    for (int i = m,j=0; i > 0; i--,j++)//取的每一个数都是能取数中,最大的(贪心)
    {
        sum += A[j];
    }
    cout << sum;
    return 0;
}

这是我做过的算法题中最简单的,没有之一!

ALGO-999 数的潜能

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  将一个数N分为多个正整数之和,即N=a1+a2+a3+…+ak,定义M=a1*a2*a3*…*ak为N的潜能。
  给定N,求它的潜能M。
  由于M可能过大,只需求M对5218取模的余数。
输入格式
  输入共一行,为一个正整数N。
输出格式
  输出共一行,为N的潜能M对5218取模的余数。
样例输入
10
样例输出
36
数据规模和约定
  1<=N<10^18
 
 为什么要分成3? 正整数分解使得乘积最大问题_小拳头的博客-CSDN博客_整数拆分乘积最大问题
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define MOD 5218
int qpow(int a, ll n)//递归实现快速模平方法
{
    if (n == 0)
        return 1;
    else if (n % 2 == 1)
        return qpow(a, n - 1) * a % MOD;
    else
    {
        int temp = qpow(a, n / 2);
        return temp * temp % MOD;
    }
}
int main()
{
    ll n, h, m;
    int result;
    cin >> n;
    h = n / 3;//存有好多个3
    m = n % 3;//余数
    if (m == 1 && h > 0)
    {
        m = 4;
        h = h - 1;
    }
    if (m != 0)
        result = qpow(3, h) * m % MOD;
    else
        result = qpow(3, h);
    cout <<result<< endl;
}
 
 

ALGO-998 娜神平衡

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  娜娜是一个特别可爱的女孩子,作为学神的她最近在情感方面出现了一点点小问题。
  她暗恋的琦琦是一名学霸,他只喜欢长得漂亮和学习很好的女生。
  娜娜学习确实很神,但是她在琦琦面前却总是表现不出平时的神力。
  琦琦感受到了娜娜对他的爱,但是他还是觉得娜娜的学习并不是特别好,于是他出了一道题给娜娜。
  “娜娜,我们之间的关系需要在不断深入的同时保持一定的平衡,不可以你总是强势或者我总是弱势。”
  琦琦给了娜娜一些两两不等的数,希望娜娜能把这些数分成两组A和B,满足以下条件:
  1:每一次只能操作一个数,即只取出一个数分入A中或B中;
  2:每一次操作完成后,A中数之和与B中数之和的差不能超过r。
  新时代的丘比特们啊,帮帮娜娜吧!
输入格式
  输入共两行。
  第一行包括两个正整数n和r,n表示琦琦一共给了n个数,r的意义见题目描述。
  第二行包括n个正整数,分别表示琦琦给的n个数。
输出格式
  输出共两行,分别把A与B两组数按从小到大输出。
  注意输入中n个数的第一个必须分入A组。
  琦琦保证这样的输出唯一。
样例输入
4 10
9 6 4 20
样例输出
4 6 9
20
样例说明
  先把4和6先后分入A组,再把20分入B组,最后把9分入A组。
数据规模和约定
  很小,真的很小。
 思想: 
  1. 排序数据,便于枚举判断
  2. 使用队列存储数据,枚举失败的数据可以放回队尾,下次再枚举
  3. 使用栈存储枚举成功的顺序,便于回溯(正因为把顺序保存了下来,免于用递归)
  4. 题目要求第一个数只能放在A中,因此,只要发现B中存在第一个数,就把B当成A就行了(由答案的唯一性,可以知道一个组中的数永远待在一起,因此只要把存有第一个数的组当成A就可以了,没必要纠结于优先枚举进哪个数组)
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
int main()
{
    //sum1存lst1所有元素的和,sum2同理;
    int n, r,sum1=0,sum2=0;
    cin >> n >> r;
    vector<int>lst(n),lst1,lst2,per;
    /*per数组存的元素含义:1表示lst元素存进lst1,
    2表示lst元素存进了lst2;*/
    for (int i = 0; i < lst.size(); i++)
    {
        cin >> lst[i];
    }
    int first = lst[0];//题目要求输入的第一个数要存进输出的第一个数组
    sort(lst.begin(), lst.end());//从小到大
    queue<int> q;//建立队列
    for (int i = 0; i < lst.size(); i++)
    {
        q.push(lst[i]);//进入队列
    }
    int t=0;
    while (!q.empty())
    {
        t = q.front();
        lst1.push_back(t);//进入lst1;
        sum1 += t;
        per.push_back(1);
        q.pop();
        if ((sum1 - sum2) > r)//如果数进入lst1后,sum1-sum2>10
        {
            lst1.pop_back();//出队
            per.pop_back();
            sum1 -= t;
            lst2.push_back(t);//入队
            per.push_back(2);
            sum2 += t;
        }
        if ( sum2 - sum1 > r)//入队到lst2后,如果还是不满足条件
        {
            q.push(t);
            lst2.pop_back();//出队
            sum2 -= t;
            per.pop_back();
            if (per.back() == 1)//如果上一次入队到lst1;
            {
                per.pop_back();
                int m=lst1.back();//回溯
                lst1.pop_back();
                q.push(m);//再入队
                sum1 -= m;

            }
            else //如果上一次入队到lst1;
            {
                per.pop_back();
                int m = lst2.back();
                lst2.pop_back();//回溯
                q.push(m);//再入队
                sum2 -= m;
            }
        }
    }
    sort(lst1.begin(), lst1.end());//排序
    sort(lst2.begin(), lst2.end());//排序
    for (int i = 0; i < lst2.size(); i++)//判断first是否再lst2里面
    {
        if (first == lst2[i])
        {
            swap(lst1, lst2);//如果在,则交换数组
            break;
        }
    }
    for (int i = 0; i < lst1.size(); i++)
        cout << lst1[i] << "  ";
    cout << endl;
    for (int i = 0; i < lst2.size(); i++)
        cout << lst2[i] << "  ";
    return 0;
}

参考链接:蓝桥杯 ALGO-998 娜神平衡(简单粗暴) - mosqu1to - 博客园 (cnblogs.com)

 

ALGO-997 粘木棍

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  有N根木棍,需要将其粘贴成M个长木棍,使得最长的和最短的的差距最小。
输入格式
  第一行两个整数N,M。
  一行N个整数,表示木棍的长度。
输出格式
  一行一个整数,表示最小的差距
样例输入
3 2
10 20 40
样例输出
10
数据规模和约定
  N, M<=7
 方法:深度遍历
#include<iostream>
#include<algorithm>
using namespace std;
int temp, sum[9], flag[9] = { 0 };
/*temp 最大值和最小值的最大差距,
sum[i] 第i组木棍总长度
falg 标准数组,判断木棍是否被分配到某一组
*/
int lst[9], n, m;
int pos;//遍历lst数组的当前位置
void DFS(int pos)
{
    if (pos > n)
    {
        int s1 = sum[1],s2 = sum[1];
        for (int i = 1; i <= m; i++)
        {
            s1 = min(s1, sum[i]);
            s2 = max(s2, sum[i]);
        }
        temp = min(s2 - s1, temp);
    }
    for ( int t = pos; t <= n; t++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (flag[t])
                break;
            flag[t] = 1;
            sum[j] += lst[t];
            DFS(pos + 1);
            //回溯
            flag[t] = 0; 
            sum[j] -= lst[t];
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> lst[i];
    sort(lst, lst + n+1);//排序的目的只是找出最大值和最小值而已,不排也可
    temp = lst[n] - lst[1];//初始temp的值为lst数组中,最大值-最小值
    DFS(1);
    cout << temp;
    return 0;
}

 

ALGO-996 车的放置

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  在一个n*n的棋盘中,每个格子中至多放置一个车,且要保证任何两个车都不能相互攻击,有多少中放法(车与车之间是没有差别的)
输入格式
  包含一个正整数n
输出格式
  一个整数,表示放置车的方法数
样例输入
2
样例输出
7
数据规模和约定
  n<=8
  【样例解释】一个车都不放为1种,放置一个车有4种,放置2个车有2种。
 
什么叫两个车都不能相互攻击?就是车两两之间,车的行和列不能相同!我之前连题都没看懂
 
#include<iostream>
using namespace std;
int ans = 1;//输出结果,好多种方法
int n;
bool flag[10] = { 0 };//标志符号,0表示第i列没放小车
void DFS(int xx)//深度遍历
{
    if (xx > n)//递归,就一定得有一个跳出递归的条件
        return;
    for (int i =1; i <= n; i++)
    {
        if (!flag[i])//如果第i列没放
        {
            flag[i] = true;
            ans++;//为什么放一辆小车就+1呢?
            //放了一辆小车就改变了这个方阵上一次的状态,就是一种放法
            DFS(xx + 1);//下一列
            flag[i] = false;//回溯
        }
    }
    DFS(xx + 1);//然后又从第二列、第三列开始放
}
int main() 
{
    cin >> n;
    DFS(1);//从第一列开始放
    cout << ans;
    return 0;

}

ALGO-995 24点

资源限制
时间限制:1.0s   内存限制:256.0MB
问题描述
  24点游戏是一个非常有意思的游戏,很流行,玩法很简单:给你4张牌,每张牌上有数字(其中A代表1,J代表11,Q代表12,K代表13),你可以利用数学中的加、减、乘、除以及括号想办法得到24,例如:
  ((A*K)-J)*Q等价于((1*13)-11)*12=24
  加减乘不用多说了,但除法必须满足能整除才能除!这样有一些是得不到24点的,所以这里只要求求出不超过24的最大值。
输入格式
  输入第一行N(1<=N<=5)表示有N组测试数据。每组测试数据输入4行,每行一个整数(1到13)表示牌值。
输出格式
  每组测试数据输出一个整数,表示所能得到的最大的不超过24的值。
样例输入
3
3
3
3
3
1
1
1
1
12
5
13
1
样例输出
24
4
21
 
posted @ 2022-02-07 18:14  Grit_L。  阅读(1061)  评论(0编辑  收藏  举报