Coin Stack——Java版详解

题目描述

A and B are playing a collaborative game that involves n stacks of coins, numbered from 1 to n. Every round of the game, they select a nonempty stack each, but they are not allowed to choose the same stack. They then remove a coin from both the two selected stacks and then the next round begins.

The players win the game if they manage to remove all the coins. Is it possible for them to win the game, and if it is, how should they play?

输入描述:

 

The first line of input contains an integer n (2 ≤ n ≤ 50), the number of coin stacks. Then follows a line containing n nonnegative integers a1, a2, . . . , an, where ai is the number of coins in the i’th stack. The total number of coins is at most 1000.

输出描述:

 

If the players can win the game, output a line containing “yes”, followed by a description of the moves. Otherwise output a line containing “no”. When describing the moves, output one move per line, each move being described by two distinct integers a and b (between 1 and n) indicating that the players remove a coin from stacks a and b. If there are several possible solutions, output any one of them.

示例1

输入

3

1 4 3

输出

yes
1 2
2 3
2 3
2 3

示例2

输入

3
1 1 1

输出

no

原题链接:https://ac.nowcoder.com/acm/contest/18456/C
来源:牛客网

原文翻译

A和B正在玩一个合作游戏,游戏中涉及n个硬币堆,编号从1到n。然后,他们从两个选定的堆栈中取出一枚硬币,然后开始下一轮游戏。

第一行输入包含一个整数n(2≤n≤50),即硬币堆的数量。然后是一行包含n个非负整数a1, a2, ...  , an,其中ai是第i个硬币堆中的硬币数量。硬币的总数最多为1000。

如果玩家能够赢得比赛,则输出包含 "是 "的一行,后面是对棋步的描述。否则就输出包含 "否 "的一行。在描述动作时,每行输出一个动作,每个动作由两个不同的整数a和b(介于1和n之间)描述,表示玩家从堆栈a和b中取出一枚硬币。

心路历程

第一次解题:

第一次尝试解题时想的比较简单,首先既然一次拿俩个,拿硬币数一定要先是偶数才成立;然后就是取硬币的过程,起初认为只要用两个指针分别指向两个钱币数不为0的洞上,让其不相遇然后循环取钱币,直至钱币取完。

这种思路通过48%的样例,原因是操作思路太简单,指针的运动是历遍顺序,缺乏机动性:

这里举一个栗子:

1 2 3 4

0 1 3 4

0 0 2 4

0 0 1 3

0 0 0 3

按照原来的思想用俩指针找硬币数不为0的两个洞取出,会出现指针相遇而判no的情况,实际上该样例可以通过。

第二次解题:

经过上一次的教训,我认定两指针的运动一定要满足弄种规律

这时我结合了实际,将自己代入题中;假如我们现在在玩这样一个游戏,想要一次在不同的洞里取出两个硬币,我们肯定不可能一根筋,哪有就找那,那样太没有大局观。

正常的我们一定会有一个预判思想,去保证至少有两个洞中硬币数不为0,避免最后只剩一个洞有硬币,而失败的结局。

这时,我就有了想法,类似一种分苹果的思想,要去保证每个人都感到合理,我们一般都会让苹果多的匀给少的。在本题中,最多和最少就是解题的关键所在。

举个栗子:

对于这个样例:1 1 6 8 9 11

我们为了去保证至少有两个洞中硬币数不为0;每次找一个最大最小值,让其下标不同,这样就保证了这一点;紧接着每次都从最大最小值对应的洞口取出该轮的两枚硬币,直至取完,便可完成过程。下面是该样例的模拟操作步骤:

1 1 6 8 9 11
0 1 6 8 9 10
0 0 6 8 9 9
0 0 5 8 9 8
0 0 4 8 8 8
0 0 3 8 8 7
0 0 2 8 7 7
0 0 1 7 7 7
0 0 0 7 7 6
0 0 0 7 6 6
0 0 0 6 6 6 
0 0 0 5 6 5
0 0 0 4 5 5
0 0 0 3 5 4
0 0 0 2 4 4
0 0 0 1 4 3
0 0 0 0 3 3
0 0 0 0 2 2
0 0 0 0 1 1
0 0 0 0 0 0

(这里要提示的一点是:当min=max时,要保证其对应的下标是不同的才能继续)

代码块:


import java.io.*;

public class Main {
    static StreamTokenizer r;
    static PrintWriter pr;
    static BufferedReader re;
    static boolean isFlag = true;

    static {
        r = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        pr = new PrintWriter(new OutputStreamWriter(System.out));
        re = new BufferedReader(new InputStreamReader(System.in));
    }


    static int maxcursor;
    static int mincursor;
    static int Thecount;
    static int index = 0;

    static int cc[];/*记录数据*/
    static int nec[];/*记录答案*/

    public static void main(String[] args) throws IOException {
        int a = ini();
        cc = new int[a];

        for (int i = 0; i < cc.length; i++) {
            cc[i] = ini();
            Thecount += cc[i];
        }
        nec = new int[Thecount];

        if (Thecount % 2 == 0) {
            while (Thecount != 0) {
                 maxMin(cc);
                if (maxcursor == mincursor) {
                    isFlag = false;
                    break;
                }
               
                
            }
            if (isFlag) {
                System.out.println("yes");
                for (int i = 0; i < nec.length; i += 2) {
                    System.out.println((nec[i] + 1) + " " + (nec[i + 1] + 1));
                }
            } else {
                System.out.println("no");
            }

        } else {
            System.out.println("no");
        }


    }

    static int ini() throws IOException {
        r.nextToken();
        return (int) r.nval;
    }

    static void maxMin(int a[]) {//对于每个回合,我们都要找到数组的最大最小值
        int b[] = a;
        int max = -1;
        int min = 1001;//注意点
        int index1 = -2;
        int index2 = -1;
        for (int i = 0; i < b.length; i++) {
            if (b[i] == 0) {
                continue;
            } else {
                if (b[i] < min) {
                    min = b[i];
                    index1 = i;
                }
                if (b[i] >= max) {
                    max = b[i];
                    index2 = i;
                }
            }
        }
        mincursor = index1;
        maxcursor = index2;

        nec[index] = mincursor;
        cc[mincursor]--;
        index++;

        nec[index] = maxcursor;
        cc[maxcursor]--;
        index++;

        Thecount -= 2;
    }

}

留在最后

这个题我想吐槽一点的时,“The total number of coins is at most 1000.”以及“The first line of input contains an integer n (2 ≤ n ≤ 50)”这两段话让我深感无语。

按理说n>=2,那么对于任意洞口,它的硬币数不会超过999;但当我们把min设为1000时,在部分案例会出现:数组越界的情况。

 分析了下,可能情况就是某个样例的硬币数没有小于1000的,导致在找最小值下标时未得到结果,mincursor直接等于了初始值-2;导致超出了数组cc的下标范围。

 

当我们把min的初始值改为1001时,问题得到了解决。


 

 

 

 

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值