【代码思维锻炼】关于一个简单逻辑题编码实现的思考

oh_Maxy 2013-12-13 02:49:41
加精
题目:两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。

这是个简单的逻辑题,寻常小学生也能分分钟做出来,但是“编程序”实现,却让我为之一愣。
这个简单问题代表了一类问题:在有限的集合以及有限的条件限定下,求一组解
让我愣神的原因主要有如下几点:
1. 给出的规则只是客观陈述,没有什么规律性可供总结
2. 程序具有重复性、客观性,不会像人那样有择优的思考倾向,本题没有什么重复性可言
3. 如何通过代码实现规则,让这个问题能解决,却又不局限于这一种场景(假设队员多了,或者限定条件多了)

由这个题目,又想到前两天百度百科看到的八皇后问题的java源码。短短四五十行代码,就实现了,让我这个写了几百行都没实现好的人,为之汗颜。现在想来,它之所以精简,一方面明确判定条件,另一方面是因为他把问题抽象的好,不慌不忙,很清晰的知道自己要实现什么。有的时候,同样一个算法,能够很好的抽象出问题的本质,会大大精简代码。而我碰到比较“麻烦的问题”时,常常会头皮一炸,然后东戳一下,西搞一下,思路不清晰,结果总是不那么理想。

废话了那么多,这里就是想针对这么个简单的乒乓球问题,看看大家伙是怎么个思路实现的,如果问题扩展了(例如队员数增多,或条件多了),对代码冲击大不大。大家一起讨论讨论,学习学习,看看有没有比较好的思路。
(我的一个实现已经写好了,和大家探讨一下再拿出来看看吧)

大家可以文字描述自己的建模思路,或算法描述,也可以直接上代码(代码关键步骤最好稍微带点注释说明),一起讨论各自思路、算法、代码的可行性、合理性。

月底结贴。

【2013-12-20】
这边也给出一个之前的代码实现,欢迎指正,欢迎大家继续讨论~

  1. import java.util.ArrayList;
  2. import java.util.HashMap;
  3. import java.util.List;
  4. import java.util.Map;

  5. public class PingpongGame {

  6. // 选手集
  7. private static final List<String> teamA = new ArrayList<String>();
  8. private static final List<String> teamB = new ArrayList<String>();

  9. // 结果集:teamA队员为key,teamB队员为value
  10. private static final Map<String, String> result = new HashMap<String, String>();

  11. // 规则集:不允许集,teamA某个队员为key,不允许的对手名单列表为value
  12. private static final Map<String, List<String>> noMap = new HashMap<String, List<String>>();

  13. // 规则集初始化
  14. static {
  15. // 队员初始化
  16. teamA.add("a");
  17. teamA.add("b");
  18. teamA.add("c");

  19. teamB.add("x");
  20. teamB.add("y");
  21. teamB.add("z");

  22. // a说他不和x比
  23. List<String> aNoList = new ArrayList<String>();
  24. aNoList.add("x");
  25. noMap.put("a", aNoList);

  26. // b的对手任意
  27. List<String> bNoList = new ArrayList<String>();
  28. noMap.put("b", bNoList);

  29. // c说他不和x,z比
  30. List<String> cNoList = new ArrayList<String>();
  31. cNoList.add("x");
  32. cNoList.add("z");
  33. noMap.put("c", cNoList);
  34. }

  35. public static void main(String[] args) {

  36. // 最终要得到的结果,是三队比赛场次的输出
  37. for (;;) {
  38. // 跳出循环的条件:形成三组对抗
  39. if (result.size() == teamA.size()) {
  40. break;
  41. }

  42. // 遍历A组学员
  43. for (String member : teamA) {
  44. // 若当前队员已经有对手,则跳过本次循环
  45. if (null != result.get(member)) {
  46. continue;
  47. }

  48. // 判断当前成员的不可能对手集,若teamB去掉此集合后,仅剩下一个队员,则录入结果集
  49. checkNoListAnd(member, noMap.get(member));
  50. }
  51. }

  52. // 打印结果
  53. for (Map.Entry<String, String> entry : result.entrySet()) {
  54. System.out.println("A组选手" + entry.getKey() + "的对手为B组选手" + entry.getValue());
  55. }
  56. }

  57. // 判断当前成员的不可能对手集,若teamB去掉此集合后,仅剩下一个队员,则录入结果集
  58. private static void checkNoListAnd(String member, List<String> noList) {
  59. // 不可能结果集为空,则不处理
  60. if (null == noList || 0 == noList.size()) {
  61. return;
  62. }

  63. // 若teamB去掉此集合后,仅剩下一个队员,则录入结果集
  64. List<String> teamBTmp = new ArrayList<String>(teamB);
  65. teamBTmp.removeAll(noList);
  66. if (1 == teamBTmp.size()) {
  67. result.put(member, teamBTmp.get(0));
  68. addMayBeList(teamBTmp);
  69. }
  70. }

  71. // 已被占用的teamB队员,加入到所有成员的不可能对手集中
  72. private static void addMayBeList(List<String> noBeList) {
  73. for (Map.Entry<String, List<String>> entry : noMap.entrySet()) {
  74. // 避免重复加入
  75. if (!entry.getValue().containsAll(noBeList)) {
  76. entry.getValue().addAll(noBeList);
  77. }
  78. }
  79. }
  80. }
...全文
给本帖投票
5536 104 打赏 收藏 转发到动态 举报
写回复
用AI写文章
104 条回复
切换为时间正序
请发表友善的回复…
发表回复
_Tango 2015-07-14
  • 打赏
  • 举报
回复
引用 84 楼 was22491686 的回复:
int【】【】 ac=new int 【3】【3】; ac={0,1,1 1,1,1 0,1,0} int i,j,k=0,m=0; for(int n=0;n<3;n++) for(i=0;i<3;i++) for(j=0;j<3;j++) if(ac[i][j]=0) { k++; if(k=2) { if(j=1) {for(int p=0;p<3;p++) ac[p][2]=0; ac[i][2]=1; } else {for(int p=0;p<3;p++) ca[p][1]=0; ca[i][1]=1; } m++; } } {}{}{}{}{}{}{}{}{}找出对手矩阵了吧{}{}{}{}{}{}{}
哈哈,翻出来那时候贴的代码,真是乱来呀,不过当时的思路还记得,思路是对的,现在重写了代码发上来。

#include <stdio.h>

#define UAN 2 //非对手
#define AN 1  //对手
#define UNKNOWN 0 //未知

char a[] = { UAN, UNKNOWN, UNKNOWN };
char b[] = { UNKNOWN, UNKNOWN, UNKNOWN};
char c[] = { UAN, UNKNOWN, UAN};

int hor_sum(char *arr)
{
        return arr[0] + arr[1] + arr[2];
}

int ver_sum(int col)
{
        return a[col] + b[col] + c[col];
}

int find_hor_unknown(char *arr)
{
        return !arr[0]? 0 : (!arr[1]? 1:2);
}

int find_ver_unknown(int col)
{
        return !a[col]? 0 : (!b[col]? 1:2);
}

void set_ua(char *arr,int pos)
{
        arr[0] = arr[1] = arr[2] = UAN;
        a[pos] = UAN;
        b[pos] = UAN;
        c[pos] = UAN;
        arr[pos] = AN;
}

void show_ua()
{
        int i;
        for(i = 0;i < 3;i++) {
                if(a[i] == AN)
                        printf("A Against %c\n",i + 88);
                if(b[i] == AN)
                        printf("B Against %c\n",i + 88);
                if(c[i] == AN)
                        printf("C Against %c\n",i + 88);
        }
}

int main()
{
        int i,j;
        for(i = 0;i < 3;i++) {
                if(hor_sum(a) == 4) {
                        set_ua(a,find_hor_unknown(a));
                }
                if(hor_sum(b) == 4) {
                        set_ua(b,find_hor_unknown(b));
                }
                if(hor_sum(c) == 4) {
                        set_ua(c,find_hor_unknown(c));
                }
                for(j = 0;j < 3;j++) {
                        if(ver_sum(j) == 4) {
                                int pos = find_ver_unknown(j);
                                if(pos == 0)
                                        set_ua(a,j);
                                else if(pos == 1)
                                        set_ua(b,j);
                                else
                                        set_ua(c,j);
                        }
                }
        }
        show_ua();
}

不良制品 2014-01-06
  • 打赏
  • 举报
回复
引用 107 楼 lwb314 的回复:
那你遍历的思路是什么呢?题目的关键就是如何遍历吧
遍历的话可以先将输入的数组放入数组容器中(array<>),之后在写入比对的数组 如之前的 0 1 2 1 0 1 2 2 1 就可以是3个数组 1 2 0 1 2 1 这里在列举1个遍历的情况(用迭代会比较方便) 假设C为输出用数组 1. C[0] = 1;C[1] = 0;C[2] = 1; -> c[2]与C[0]重复;舍去。 C[2]数组完结;返回 2. c[0] = 1;C[1] = 1;C[2] = 1; -> c[2]与C[0]重复;舍去。 C[2]数组完结;返回 ... n. c[0] = 2;c[1] = 0;c[2] = 1; ->正确,输出 大致就是这样。这只是个大概的思路,具体的实现还可以看下用什么方法优化下
  • 打赏
  • 举报
回复
引用 108 楼 u011623102 的回复:
[quote=引用 107 楼 lwb314 的回复:] 那你遍历的思路是什么呢?题目的关键就是如何遍历吧
遍历的话可以先将输入的数组放入数组容器中(array<>),之后在写入比对的数组 如之前的 0 1 2 1 0 1 2 2 1 就可以是3个数组 1 2 0 1 2 1 这里在列举1个遍历的情况(用迭代会比较方便) 假设C为输出用数组 1. C[0] = 1;C[1] = 0;C[2] = 1; -> c[2]与C[0]重复;舍去。 C[2]数组完结;返回 2. c[0] = 1;C[1] = 1;C[2] = 1; -> c[2]与C[0]重复;舍去。 C[2]数组完结;返回 ... n. c[0] = 2;c[1] = 0;c[2] = 1; ->正确,输出 大致就是这样。这只是个大概的思路,具体的实现还可以看下用什么方法优化下[/quote]觉得还是结合题的情况来说才比较好理解,而且你的第2个条件是你自己写出来的,题里并没有吧
别闹腰不好 2014-01-02
  • 打赏
  • 举报
回复
都月出啦 说好的月底呢
  • 打赏
  • 举报
回复
引用 102 楼 u011623102 的回复:
我这里只提供一个思路。 将队伍写在两个数组中,假如是A、B。 然后分别输入A[n]与B中的那些可能会比赛。 最后更具输入的来遍历输出结果就可以了。 如原题中可以是 a[3] = {a,b,c} b[3] = {x,y,z} 输入如下: 0 1 2 1 0 1 2 2 1 最后预测输出会是: 0 2 1 0 2 1 如果是控制太界面的化输入的处理会比较麻烦,是窗口界面的话会好点
那你遍历的思路是什么呢?题目的关键就是如何遍历吧
  • 打赏
  • 举报
回复
好多高手哇。。。。。。。。。。。。。。。
Lena-Yang 2014-01-02
  • 打赏
  • 举报
回复
好多算法。马克一下抽空再研究。算法现在比较喜欢
HinanaiTenshi 2013-12-31
  • 打赏
  • 举报
回复
引用 76 楼 oh_Maxy 的回复:
[quote=引用 69 楼 HinanaiTenshi 的回复:] 好像不能直接上传代码包啊,看来只能一个一个类帖上来了。
不说别的,你的面向对象的思想,建模工具的使用,以及功能可扩展性的预见,很值得我学习。 此外还有注释,它是体现程序员编码过程中的思维过程,是代码可用性的一个要素,这也是值得看重的地方。 这边也大致看了下各个类,对于算法上面,建议在全排列过程中,考虑某一对选手确定后,对其他队员选择对手的影响,会简化一些不必要的排列。 还有编码上的一些小细节,可以注意改良下,比如一个类的定义,先写成员属性,再写构造方法,最后写其它成员方法。 最后提醒一下:练习归练习,如果工作中碰到类似简单的业务类问题,建议不要过于发散。 当然,如果从事平台、组件开发工作,这种发散思维难能可贵。 非常感谢兄台的分享! [/quote] 算法上版主说得对,完全剥离算法和排除接口是很坏的设计。我最初是希望算法和排除接口解耦,结果实际执行发现效率很低下。现在想想更好的设计是把排除接口也传给算法,这样保持耦合度低的同时,能利用排除接口优化算法。 另外发现接口设计得也很奇葩,明明都写了是init方法,居然直接扔到接口里去了,完全没想起来用工厂类。 编码的排列其实是自动排版的结果,每次保存的时候方法和属性顺序被整理了。 说到工作,非常赞同版主的意见,特别是针对一些紧急的任务,不能想得太多,公司和管理者是看中结果的。 不过能多考虑一点就多一点好处,将来维护和升级省了许多麻烦。个人经验是后期只恨当初想不够,偶尔余留几个多余的设计,那也是设计上想太少,升级才会用不上。
不良制品 2013-12-31
  • 打赏
  • 举报
回复
我这里只提供一个思路。 将队伍写在两个数组中,假如是A、B。 然后分别输入A[n]与B中的那些可能会比赛。 最后更具输入的来遍历输出结果就可以了。 如原题中可以是 a[3] = {a,b,c} b[3] = {x,y,z} 输入如下: 0 1 2 1 0 1 2 2 1 最后预测输出会是: 0 2 1 0 2 1 如果是控制太界面的化输入的处理会比较麻烦,是窗口界面的话会好点
鲁小 2013-12-26
  • 打赏
  • 举报
回复
我老感觉有某种现成的数据结构能很方便地解决这个问题
LONEKUN1 2013-12-25
  • 打赏
  • 举报
回复
不是很懂,有点晕
oh_Maxy 2013-12-25
  • 打赏
  • 举报
回复
引用 91 楼 ljzms1212 的回复:
测试1 ,和测试2。 第一个是{x,z},{x,y,z}{z},第二个是{y,z}{x,y,z},{z},题目的是第二种情况。 只是测试而已。有时候不会只有一个解的。。
嗯看来我误解了。 再问下:比如c已经确认和z对阵了,那么对于a、b可能的对手集是否有影响,需要将z从中剔除?感觉这样会简化操作的~
linuxca 2013-12-25
  • 打赏
  • 举报
回复
好,
oh_Maxy 2013-12-25
  • 打赏
  • 举报
回复
引用 99 楼 u013276062 的回复:

String[] arr1 = {"a","b","c"};
		String[] arr2 = {"x","y","z"};

		Map<String,List<String>> map = new LinkedHashMap<String,List<String>>();
		
		for(int i = 0; i < arr1.length; i ++) {
			for(int k = 0; k < arr2.length; k ++) {
		      
			   if("a".equals(arr1[i]) && "x".equals(arr2[k])){
				   continue;
			   }
			   else if("c".equals(arr1[i]) && ("x".equals(arr2[k]) || "z".equals(arr2[k]))) {
				   continue;
			   }
			   else {
				   if(map.containsKey(arr1[i])) {
					   map.get(arr1[i]).add(arr2[k]);
				   }
				   else {
					  List<String> list = new ArrayList<String>();
					  list.add(arr2[k]);
					  map.put(arr1[i], list);
				   }
			   }
		   }
		}
		
		for(Entry<String, List<String>> entry1 : map.entrySet()) {
			List<String> value1 = entry1.getValue();
			for(Entry<String, List<String>> entry2 : map.entrySet()) {
				List<String> value2 = entry2.getValue();
				
				if(value1 == value2) {
					continue;
				}
				else if(value1.size() == 1) {
					map.put(entry1.getKey(), value1);
					break;
				}
				else if(value1.size() >= value2.size()){
					value1.removeAll(value2);
				}
				else if(value2.size() < value2.size()) {
					value2.removeAll(value1);
					map.put(entry2.getKey(), value2);
				}
			}
			
			map.put(entry1.getKey(), value1);
			System.out.println(entry1.getKey() + value1);
		}
好像很麻烦的样子
兄弟这样的算法,不够灵活,都是按条件写死了的。如果改变了条件,对代码影响很大。建议参考下LS们的,看看有什么差别。
萧逸尘 2013-12-25
  • 打赏
  • 举报
回复

String[] arr1 = {"a","b","c"};
		String[] arr2 = {"x","y","z"};

		Map<String,List<String>> map = new LinkedHashMap<String,List<String>>();
		
		for(int i = 0; i < arr1.length; i ++) {
			for(int k = 0; k < arr2.length; k ++) {
		      
			   if("a".equals(arr1[i]) && "x".equals(arr2[k])){
				   continue;
			   }
			   else if("c".equals(arr1[i]) && ("x".equals(arr2[k]) || "z".equals(arr2[k]))) {
				   continue;
			   }
			   else {
				   if(map.containsKey(arr1[i])) {
					   map.get(arr1[i]).add(arr2[k]);
				   }
				   else {
					  List<String> list = new ArrayList<String>();
					  list.add(arr2[k]);
					  map.put(arr1[i], list);
				   }
			   }
		   }
		}
		
		for(Entry<String, List<String>> entry1 : map.entrySet()) {
			List<String> value1 = entry1.getValue();
			for(Entry<String, List<String>> entry2 : map.entrySet()) {
				List<String> value2 = entry2.getValue();
				
				if(value1 == value2) {
					continue;
				}
				else if(value1.size() == 1) {
					map.put(entry1.getKey(), value1);
					break;
				}
				else if(value1.size() >= value2.size()){
					value1.removeAll(value2);
				}
				else if(value2.size() < value2.size()) {
					value2.removeAll(value1);
					map.put(entry2.getKey(), value2);
				}
			}
			
			map.put(entry1.getKey(), value1);
			System.out.println(entry1.getKey() + value1);
		}
好像很麻烦的样子
  • 打赏
  • 举报
回复
引用 97 楼 hankcs 的回复:
原来是穷举,的确比lz的短
------------------------------------------------------AutoCSDN签名档------------------------------------------------------
[url=http://www.hankcs.com/program/]码农场——码农播种代码、放牧思想的农场。 [quote=引用 1 楼 huxiweng 的回复:] 怎么都没人? 算法我很弱。这种题我一般是先推出最小范围,然后再枚举。 抛砖引玉吧:

char[] chars = { 'x','y','z' };

		for (int i = 0; i < chars.length; i++) {
			for (int j = 0; j < chars.length; j++) {
				for (int k = 0; k < chars.length; k++) {
					char a = chars[i];
					char b = chars[j];
					char c = chars[k];
					
					// 满足所有条件
					if ((a != b) && (b!=c) && (a!=c) & (a!='x') && (c!='x') && (c!='z')) {
						System.out.println("a," + a);
						System.out.println("b," + b);
						System.out.println("c," + c);
					}
				}
			}
		}
[/quote] 他不是楼主骚年
hankcs 2013-12-25
  • 打赏
  • 举报
回复
原来是穷举,的确比lz的短
------------------------------------------------------AutoCSDN签名档------------------------------------------------------
码农场——码农播种代码、放牧思想的农场。
引用 1 楼 huxiweng 的回复:
怎么都没人? 算法我很弱。这种题我一般是先推出最小范围,然后再枚举。 抛砖引玉吧:

char[] chars = { 'x','y','z' };

		for (int i = 0; i < chars.length; i++) {
			for (int j = 0; j < chars.length; j++) {
				for (int k = 0; k < chars.length; k++) {
					char a = chars[i];
					char b = chars[j];
					char c = chars[k];
					
					// 满足所有条件
					if ((a != b) && (b!=c) && (a!=c) & (a!='x') && (c!='x') && (c!='z')) {
						System.out.println("a," + a);
						System.out.println("b," + b);
						System.out.println("c," + c);
					}
				}
			}
		}
ljzms1212 2013-12-24
  • 打赏
  • 举报
回复
引用 90 楼 oh_Maxy 的回复:
[quote=引用 89 楼 ljzms1212 的回复:]
a说他不和x比,c说他不和x,z比,其实就是 a可能和y,z比,c可能和y比。
那我直接考虑用{y,z},{x,y,z},{z},即可,我是上过几天培训班,一年经验不到的菜鸟,代码不好请勿嘲笑。



互相学习罢了~

继python版本之后,又来了C#版~

不过你的结果似乎有两个?实际上结果,在本题中应该是唯一的。
[/quote]
ljzms1212 2013-12-24
  • 打赏
  • 举报
回复
测试1 ,和测试2。 第一个是{x,z},{x,y,z}{z},第二个是{y,z}{x,y,z},{z},题目的是第二种情况。 只是测试而已。有时候不会只有一个解的。。
oh_Maxy 2013-12-24
  • 打赏
  • 举报
回复
引用 89 楼 ljzms1212 的回复:
a说他不和x比,c说他不和x,z比,其实就是 a可能和y,z比,c可能和y比。 那我直接考虑用{y,z},{x,y,z},{z},即可,我是上过几天培训班,一年经验不到的菜鸟,代码不好请勿嘲笑。

            
互相学习罢了~ 继python版本之后,又来了C#版~ 不过你的结果似乎有两个?实际上结果,在本题中应该是唯一的。
加载更多回复(84)

67,549

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
社区公告
暂无公告

试试用AI创作助手写篇文章吧

手机看
关注公众号

关注公众号

客服 返回
顶部