C#试玩程序设计大赛试题(1-6)

前言:锻炼为主。。。不写校验了,毕竟要是真参加比赛,做完卷或者卡主时回头再写校验和重新输入了(doge),题库链接http://www.wendangku.net/doc/d828140eb307e87101f69650.html

1、数学黑洞(≈15min)

【问题描述】
任给一个4位正整数,其各位数位上的数字不全相同,将数字重新组合成一个最大的数与最小的数相减,重复这个过程,最多7步,必得6174。对任给的4位正整数(各位数位上的数字不全相同),编程输出掉进黑洞的步数。【输入】一行,一个4位正整数n(1000< n<9999)
【输出】掉进黑洞的步数
输入:1234
输出:3

private static UInt32 GetDiff(string int1) 
{
    char[] chars = int1.ToCharArray();
    List<char> charList = chars.ToList();
    charList.Sort();// 从小到大排序
    string minInt = "";
    foreach (var item in charList)
    {
        minInt += item;
    }
    string maxInt = "";
    for (int i = charList.Count - 1; i >= 0; i--)// 反向最小值
    {
        maxInt += charList[i];
    }
    Console.WriteLine("maxInt:" + maxInt);
    Console.WriteLine("minInt:" + minInt);
    return UInt32.Parse(maxInt) - UInt32.Parse(minInt);
}

static void Main()
{
	Console.WriteLine("请输入任一4位正整数,其各位数位上的数字不全相同:" + minInt);
    string int1= Console.ReadLine();
    UInt32 blackHole = 0;
    int step = 0;
    while (true) {
        step++;
        blackHole= GetDiff(int1);
        if (step > 7) {
            Console.WriteLine("Error");
            break;
        }
        Console.WriteLine("blackHole:" + blackHole);
        Console.WriteLine();

        if (blackHole== 6174) break;
        int1 = blackHole + "";
    }
    Console.WriteLine(step);// 掉进黑洞的步数
    Console.ReadKey();
 }

2、进制转换(≈15min)

【问题描述】
任给一个十进制整数n,及正整数m(m<=16且m≠10), 将n转换成m进制并输出。
【输入】一行,两个整数n,m(0 ≤ n ≤ 500000,2 ≤ m ≤ 16,且m≠10),中间用一个空格隔开,其中n 表示十进制数。
【输出】转换后的数
【输入输出样例】
输入:255 8
输出:377

思路========================================================================
这不是Convert.ToString(n, m);一句话的事?难道这是其他语言的?这里试下逢m进1的处理算法;
在这里插入图片描述

static void Main()
{
    Console.WriteLine("请输入一个十进制的数:");
    int m = Int32.Parse(Console.ReadLine());
    Console.WriteLine("要转成多少进制的呢:");
    int n = Int32.Parse(Console.ReadLine());
    string result = "";//结果
    int quotient = 0;//商
    while (true)
    {
        quotient = m / n;
        int remainder = m % n;
        int A = 'a';
        if (remainder > 9)// 收集余数到前端
        {
            result = (Char)(remainder - 10 + A) + result;
        }
        else
        {
            result = m % n + result;
        }
        //Console.WriteLine("result:" + result);
        if (quotient == 0) break;// 最后一个余数
        m = quotient;
    }
    Console.WriteLine(result.TrimStart('0'));
    //Console.WriteLine("验证:" + Convert.ToString(m, n));
    Console.ReadKey();
}

3.分数线划定(≈30min)

【问题描述】
公务员选拔工作正在 A 市如火如荼的进行。为了选拔优秀人才,A 市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的150%划定,即如果计划录取m名公务员,则面试分数线为排名第m150%(向下取整)名的选手的分数,而最终进入面试的选手为笔试成绩不低于面试分数线的所有选手。现在就请你编写程序划定面试分数线,并输出所有进入面试的选手的报名号和笔试成绩。
【输入】
第一行,两个整数n,m(5 ≤ n ≤ 5000,3 ≤ m ≤ n),中间用一个空格隔开,其中n 表示报名参加笔试的选手总数,m 表示计划录取的人数。输入数据保证m
150%向下取整后小于等于n。
第二行到第 n+1 行,每行包括两个整数,中间用一个空格隔开,分别是选手的报名号k(1000 ≤ k ≤ 9999)和该选手的笔试成绩s(1 ≤ s ≤ 100)。数据保证选手的报名号各不相同。
【输出】
第一行,有两个整数,用一个空格隔开,第一个整数表示面试分数线;第二个整数为进入面试的选手的实际人数。
从第二行开始,每行包含两个整数,中间用一个空格隔开,分别表示进入面试的选手的报名号和笔试成绩,按照笔试成绩从高到低输出,如果成绩相同,则按报名号由小到大的顺序输出。
【输入输出样例】
输入:
6 3
1000 90
3239 88
2390 95
7231 84
1005 95
1001 88
输出:
88 5
1005 95
2390 95
1000 90
1001 88
3239 88
【样例说明】
m150% = 3150% = 4.5,向下取整后为4。保证4 个人进入面试的分数线为88,但因为88有重分,所以所有成绩大于等于88 的选手都可以进入面试,故最终有5 个人进入面试。

思路===============================================
这里主要是求出分数线然后筛选&排序就行,思路可以很发散

static void Main()
{
    Console.WriteLine("输入:");
    string[] strArray = Console.ReadLine().Split();
    int n = Int32.Parse(strArray[0]);// 总人数
    int m = Int32.Parse(strArray[1]);// 录取人数
    // 校验(5 ≤ n ≤ 5000,3 ≤ m ≤ n),保证m*150%向下取整后≤n
    Dictionary<string, double> no_marks = new Dictionary<string, double>();
    // test 
    //no_marks.Add("1000", 90);
    //no_marks.Add("3239", 88);
    //no_marks.Add("2390", 95);
    //no_marks.Add("7231", 84);
    //no_marks.Add("1005", 95);
    //no_marks.Add("1001", 88);
    //if(false)
    // test
    for (int i = 0; i < n; i++)
    {
        strArray = Console.ReadLine().Split();
        // 输入校验报名号k(1000 ≤ k ≤ 9999)和该选手的笔试成绩s(1 ≤ s ≤ 100)
        no_marks.Add(strArray[0], double.Parse(strArray[1]));
    }
    List<double> marks = no_marks.Values.ToList();
    marks.Sort();
    double m_mark = 0;// 第M名的分数
    for (int i = 0; i < (int)(m * 1.5); i++)
    {
        m_mark = marks[n - 1 - i];
    }

    var result = from A in no_marks
                 where A.Value >= m_mark
                 orderby A.Value descending,A.Key ascending
                 select A.Key + " " + A.Value;
    Console.WriteLine(m_mark+" "+result.Count());
    foreach (var item in result)
    {
        Console.WriteLine(item);
    }
    Console.ReadKey();
}

4.生日礼物(≈10min)

【问题描述】
小新今天过生日,他妈妈带他到一家购物中心,他妈妈告诉他,他今天可以在这家商店选购3件礼物,但这3件礼物必须满足条件:后面礼物的价格不得高于前面礼物的价格,如有5件商品价格分别是12,11,8,9,10,则小新挑选礼物方案只能是(12,11,8)、(12,11,9)、(12,11,10)这三种方案中的一种,小新对挑选礼物的方案很感兴趣,请编程计算挑选礼物的方案数。
【输入】
第一行,一个整数n(n≤1000),表示待选商品的总数。第二行n个整数,用空格隔开,分别表示每件商品的价格(每件商品的价格≤10000)。
【输出】
共一行,包含1 个整数,表示挑选礼物的方案数。
【输入输出样例】
输入
5
12 11 8 9 10
输出
3
思路==========================================================
为啥不能【10,9,8】?感觉这里的描述不太明确,
难道这是暗示不能跟女人谈逻辑【doge】,美雅铁拳警告~
按最后结果推理的话,有两个情况(需注意不同商品同价格的组合情况):
情况1:可选方案中前两个商品必定是价格最贵的2个商品,第三个才是最后的选择
,那么方案数的算法就是固定的【n-2】,再处理下价格第二贵的商品超过2个的情况【(n-2)*2-1】;

情况2:列出的商品价格时的排序相关,即如果列出【12 8 10 9 2】时,
由于第三个价格10 > 第二个价格 8 ,所以,这时的方案只有一个【12 8 2】,那么判断逻辑就是按排序判断大小,按价格大小规则计数方案即可;

总觉得,都不太对劲,待会对答案。。。弃题

5、资金预算(≈40min)

【问题描述】
小新的亲戚开了一家建材批发部,近来由于价格不稳定,故需要制定按月执行的仓储计划,该批发部有n个供货月份,每个月仓储预算、商品价格预算、客户需求量分别为Pi,Si,Di,,每月客户需求必须得到保证。当然,也可以在价格适当时进行囤货,即用仓库所储备的商品供货。若存货不足,就要购买商品供货,如某月要购货,则该月要加上一笔购货预算资金Ri。例如,有2个预算月份,P1,S1,D1,R1分别为0,10,100,10;P2,S2,D2,R2分别为0,100,100,10;则方案1:第一个月买够D1的货,第二个月买够D2的货,预算等于
(D1×S1+R1)+(D2×S2+R2)=(100×10+10)+(100×100+10)=11020。
方案2:第一个月买够2个月的商品,第二个月不用买货,可用囤积的商品供货,但需要支付第一个月的仓储费,预算等于
((D1+D2)×S1+R1)+(P1×D2)=((100+100)×10)+(0×100)=2010。
显然方案2优于方案1。人工进行这样的预算费时费力,请你编程帮小新亲戚安排预算从而实现利润的最大化。
【输入】
输入文件第一行是整数n(1≤ n ≤20),表示有n个月份;接着有n行,每行4个数据,分别表示 Pi,Si,Di,Ri,即仓储费用,商品价格预算,客户需求量及购货预算。其中1≤ i ≤n,0≤Pi,Si,Ri≤100000; ,∑Di≤500。【输出】
仅1行,数据是最小总预算。
样例1:
【输入1】
2
0 10 100 10
0 100 100 10
【输出1】
2010
【输入2】
2
100 10 100 10
0 100 100 10
【输出2】
11020

思路=================================================================
第一个参数控制接收数据行数,第二批参数计算并比较选择两个方案其一;
说实话,一开始没看懂,不过看懂公式;
下面是边循环边算两个方案的预算,也可以像题目的思路先循环再遍历计算;
方案一,买多少卖多少,每个月累加预算但不用交仓储费;

方案二,(趁这个月商品价格S便宜)预先买完 i 个月的需求量∑Di,
然后次月为交付仓储费(∑Di - ∑Dk )×Pi,存货渐渐卖出去所以会逐月递减,其中k=i-1;
而且因为预购了下个月的存货所以这个月不用预留金额,只需要给第一个月的预留金额R1;

也就是说第一个月没存货免仓储费,仓储费要交0次——0×D1;
仅第二个月的存货,要存一个月,仓储费要交一次——P1×D2;
仅第三个月的存货,要存两个月,仓储费要分开交两次(不同月份分别计算)——(P1+P2)×D3;
仅第四个月的存货,要存三个月,仓储费要分开交三次(不同月份分别计算)——(P1+P2+P3)×D4;

第 i 个月的存货,要存 i-1 个月,仓储费交 i-1 次——∑Pk×Di;

综上所述,方案二里每个月的存货预算为(S1+∑Pk)×Di;
总的预算为(( S1 + ∑Pk ) × Di × i + R1;

static void Main()
        {
            Console.WriteLine("输入:");
            int str = Int32.Parse(Console.ReadLine());
            int n = str;// 总数
            double plan1 = 0,// 方案一
            plan2 = 0,// 方案二
            sumP = 0,// 当前仓储费总和
            // P1,// 第一个月仓储预算
            S1 = 0,// 第一个月商品价格预算
            // D1 ,// 第一个月需求量
            R1 = 0,// 第一个月购货预算资金
            P,// 仓储预算
            S,// 商品价格预算
            D,// 需求量
            R = 0;// 最后一个月的购货预算资金
            string[] strArray;
            for (int i = 0; i < n; i++)
            {
                strArray = Console.ReadLine().Split();
                // 格式校验
                P = double.Parse(strArray[0]);// 仓储预算
                S = double.Parse(strArray[1]);// 商品价格预算
                D = double.Parse(strArray[2]);// 需求量
                R = double.Parse(strArray[3]);// 购货预算资金
                // 校验1≤ n ≤20,1≤ i ≤n,0≤Pi,Si,Ri≤100000,∑Di≤500
                if (i == 0)
                {
                    //P1 = P;
                    S1 = S;
                    //D1 = D;
                    R1 = R;
                }
                plan1 += (D * S + R);
                plan2 += (S1 + sumP) * D;
                sumP = +P;
            }
            plan2 += R1;
            Console.WriteLine(plan1 > plan2 ? plan2 : plan1);
            Console.ReadKey();
	}

6、热带鱼(≈3H)

【问题描述】
一个晴朗的星期天,小新在商场看见美丽的热带鱼,五彩斑斓的热带鱼在碧绿的水草映照下格外绚丽夺目,大大小小的鱼儿有的慢悠悠的散着步,有的疾驰如闪电,小新不禁被美丽的鱼儿所吸引,他非常想将这些美丽的小精灵们全买回家,但鱼儿的种类太多了,所以小新决定每种鱼最多只买1条,由于鱼儿间会相互争斗蚕食,故有些鱼儿是不能同时买回家的,小新想买尽可能多的鱼,但他身上的钱有限,只能在资金许可的范围内买鱼,怎么办?请你设计一个最佳方案,在资金许可的范围选择花费资金最多的一种买鱼方案。程序运行时间10s内。
【输入】
文件的第一行为两个正整数m(m≤1000)与n(n≤30),分别表示资金与鱼的种类;以下行,每行2个正整数,分别表示某种鱼的编号(编号≤30)及价格(价格≤10000);接着,每行2个正整数p与q ,表示编号p与编号q 的鱼不能共处,当p、q均等于0时,表示输入文件结束。
【输出】
输出文件为2行,分别为2个正整数,第一行整数表示所买鱼的条数,第二行整数表示所花费的资金。
样例1:
【输入1】
15 3
1 30
2 39
3 18
1 3
0 0
【输出1】

样例2:
【输入2】
180 6
6 80
5 60
4 40
3 30
2 50
1 20
1 4
3 5
5 6
0 0
【输出2】
4
180

思路=================================================================
话说,程序运行时间10s内,电脑配置不同不就结果不同?那不偷跑就是先收集输入数据再处理计算的话就,获取结果每次循环判断计时,或者开个计时线程应该搞定;但是既然要快那么题目又没说不能偷步抢跑,那么可以试着在输入的时候就开始开线程在后台算;
而且这p/q都不可能为0,直接说输入0就结束不行,算了,就当是题目要求吧…
元素数量(鱼的种类)n
组合长度 k
组合数公式:n ! / ( k ! * ( n - k ) ! )
由于是求【资金使用最大化】&【种类尽可能多】的组合,且组合里每种【最多一条】;
所以先处理排除p/q,并从组合数量大的开始排查收集;

既然两个排除处理不能独立,那么根据题意的优先符合【资金使用最大化】再从等额组合里选【种类尽可能多】的组合;可是这么设计的话,如果有条鱼刚好等于总资金(【资金使用率】100%),其他鱼的价格加起来都不够它大,那么最佳方案的种类就只有一条了,还是有点怪怪的…

每次输入分析
在这里插入图片描述
上图绿色部分就是每次新输入编号后对比起上一次输入新加的组合,也就是说每次添加的数量为2的【n-1】次幂,而且新增组合就是上次各个组合末尾添加一个新输入的编号,外加一个单编号组合也就是新编号自己。
(我们的列表/数组长度属性Count都是int,也就是2的32次幂,题目要求数量≤30理论上够用)
由于输入编号之前我们已经获取到了【总金额】m的值,我们可以把这个作为条件进一步缩窄筛选组合范围,例如上图第三次输入时发现AC组合的金额超过m那么A+C+其他的组合也必定超过【总金额】m,也就没必要考虑了,第三次输入的新增量就少了2个,而第四次输入的新增量就又少了2个,也就是每排除一个后面每n次输入都少2n个组合
按题目的数据得到下面的组合:
在这里插入图片描述

   static void Main()
    {
        bool isTest = true;// 测试标志
        string[] strArray = (isTest ? "180 6" : Console.ReadLine()).Split();
        double m = double.Parse(strArray[0]);// 总资金
        uint n = uint.Parse(strArray[1]);// 种类数
        // 校验输入值
        Dictionary<uint, double> no_prices = new Dictionary<uint, double>();// 编号,价格
        List<List<uint>> suitCombones = new List<List<uint>>();// 最终筛选的集合
        List<List<uint>> unSuitCombones = new List<List<uint>>();// 不符合的集合
        List<uint[]> excludeCombones = new List<uint[]>();// 互相蚕食组合

        double maxCombonePrice = 0;// 最佳组合的资金
        List<uint> maxCombone = new List<uint>() ;// 最佳组合
        if (isTest)
        {
            n = 30;
            Random r = new Random();
            no_prices.Add(6, 80);
            no_prices.Add(5, 60);
            no_prices.Add(4, 40);
            no_prices.Add(3, 30);
            no_prices.Add(2, 50);
            no_prices.Add(1, 20);
            excludeCombones.Add(new uint[] { 1, 4 });
            excludeCombones.Add(new uint[] { 3, 5 });
            excludeCombones.Add(new uint[] { 5, 6 });
            // 原题基础上添加
            for (uint i = 7; i <= n; i++)
            {
                no_prices.Add(i, r.Next(10, 30));
            }


            for (int i = 0; i < 6; i++)
            {
                uint p = (uint)r.Next(7, 30);
                uint q = (uint)r.Next(7, 30);
                if (!excludeCombones.Any((removeCombone)=> {
                    return removeCombone.Contains(p)&& removeCombone.Contains(q) ;
                }))
                {
                    excludeCombones.Add(new uint[] { p, q });
                }
            }
        }
        else
        {
            for (int i = 0; i < n; i++)
            {
                strArray = Console.ReadLine().Split();
                uint no = uint.Parse(strArray[0]);
                double price = double.Parse(strArray[1]);
                if (price > m) continue;// 比总金额大的直接不用考虑
                no_prices.Add(no, price);
            }
        }

        string readString = "";
        while (true && !isTest)
        {
            readString = Console.ReadLine();
            if (readString == "0 0") break;
            strArray= readString.Split();
            uint p = uint.Parse(strArray[0]);
            uint q = uint.Parse(strArray[1]);
            if (!excludeCombones.Any((excludeCombone) => {
                return excludeCombone.Contains(p) && excludeCombone.Contains(q);
            }))
            {
                excludeCombones.Add(new uint[] { p, q });
            }
        }

        Stopwatch sw = new Stopwatch();
        sw.Start();
        foreach (uint no in no_prices.Keys)
        {
            double price = no_prices[no];
            if (price > m) continue;// 比总金额大的直接不用考虑
            List<List<uint>> addList = new List<List<uint>>();
            List<List<uint>> addUnList = new List<List<uint>>();
            suitCombones.ForEach((suitCombone) => {
                List<uint> newCombone = new List<uint> { no };
                List<uint> newUnCombone = new List<uint> { no };

                bool isExclude =excludeCombones.Any((excludeCombone) => {// 是否有互斥组合形成
                    if (excludeCombone.Contains(no))
                    {
                        return excludeCombone.Any((excludeNo) => {
                            return excludeNo != no && suitCombone.Contains(excludeNo);
                        });
                    }
                    return false;
                });

                if (price > maxCombonePrice) // 优先获取【资金使用率】最高的组合
                {
                    maxCombone = newCombone;
                    maxCombonePrice = price;// 记录最大值
                }

                if (!isExclude)// 只添加不互斥的组合
                {
                    double sum = suitCombone.Sum((c) => { return no_prices[c]; }) + price;
                    if (sum <= m) // 只添加不超额的组合
                    {
                        suitCombone.ForEach((comboneNo) =>
                        {
                            newCombone.Add(comboneNo);
                        });
                        if (isTest)
                        {
                            newCombone.Sort();
                        }
                        addList.Add(newCombone);
                        
                        if (sum > maxCombonePrice) // 优先获取【资金使用率】最高的组合
                        {
                            maxCombone = newCombone;
                            maxCombonePrice = sum;// 记录最大值
                        }
                        else if (sum == maxCombonePrice && newCombone.Count > maxCombone.Count)// 【资金使用率】一样高,则再获取组合元素多的那个
                        {
                            maxCombone = newCombone;
                        }
                    }
                }
                else if (isTest)
                {
                    suitCombone.ForEach((comboneNo) =>
                    {
                        newUnCombone.Add(comboneNo);
                    });
                    //newUnCombone.Sort();
                    addUnList.Add(newUnCombone);
                }
            });

            if (isTest && false)//这个位置主要是看剔除了哪些组合
            {
                unSuitCombones.ForEach((suitCombone) =>
                {
                    List<uint> newUnCombone = new List<uint> { no };
                    suitCombone.ForEach((comboneNo) =>
                    {
                        newUnCombone.Add(comboneNo);
                    });
                    addUnList.Add(newUnCombone);
                });
            }
            addList.Add(new List<uint> { no });// 一条鱼一组
            suitCombones.AddRange(addList);

            if (isTest)
            {
                unSuitCombones.AddRange(addUnList);
            }
        }
        Console.WriteLine(maxCombone.Count);
        Console.WriteLine(maxCombonePrice);
        if (isTest)
        {
            Console.WriteLine("序号\t编号\t价格");
            for (int i = 0; i < maxCombone.Count; i++)
            {
                Console.WriteLine(i+1+"\t" + maxCombone[i] + "\t" + no_prices[maxCombone[i]]);
            }
        }
        Console.WriteLine("总耗时:" + sw.ElapsedMilliseconds + "ms");
        Console.ReadKey();
}

在这里插入图片描述
在这里插入图片描述
这里我用30个编号试了下还是可以<10s,当然如果总金额越多,每条鱼越便宜,就越难用【总金额】m排除,就像第二图那样我把每条鱼的金额压低的话,就达不到10s内完事的要求了,,所以想法错了,不是这么玩的。。。。要优化下排查的方向以及终止的条件,毕竟还是有可能会2的32次幂+的组合全排查一次,真考试就完蛋了,考虑这么久还是不完美的;

冷静了下,想想实际情况人会怎样操作,应该是从便宜的开始买,遇到互斥的要最便宜的先,然后一直买到超额前一条鱼为止,然后再讨论用互斥的鱼,去拼凑金额使用率最大的组合,但是题意是优先【金额使用率】再到【鱼种类】数量,所以这种操作会导致非常依赖最便宜的那几条鱼的金额以及互斥组合,一样有可能会检索过量导致超时,例如下图的样例虽然从数据中最多能买13条但是金额没到最大利用率,这时就要从列表里最贵的那条鱼开始跟后面贵点的鱼换直到【金额使用率】最大,而且如果不能早点换到【金额使用率】100%那么还是有可能把所有组合都检索一遍…这里代码就搞了一半就不贴出来了。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值