算法设计:找出诚实的人

zhujun88nz 2013-09-22 11:13:10
一个地方有两种人:诚实的人和骗子,诚实的人只说真话,骗子可能说真话也可能说假话,已知的是这里诚实的人比骗子要多,而且当地人知道这里其他的人是骗子还是诚实的人。
你来到这里,要求找出所有的骗子,你可以问这里任何的人A关于另外个人B的问题“B是不是骗子?”。设计一个找出所有骗子的算法,但是时间复杂度是O(n)
...全文
1001 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhujun88nz 2013-10-12
  • 打赏
  • 举报
回复
如果是先找出一个诚实的人的话,那复杂度最坏就是N^2应该是变不了的
Mourinho 2013-09-26
  • 打赏
  • 举报
回复
引用 21 楼 zoeg 的回复:
楼上凭什么把两个骗子都剔除,他们可以都撒谎
S-S,除非2人都说对方不是骗子(同HH),那么就选一个放入链表继续,丢弃另一个;否则两个都丢弃(同HS)
Mourinho 2013-09-24
  • 打赏
  • 举报
回复
引用 15 楼 lxgwm2008 的回复:
[quote=引用 3 楼 zyc13701469860 的回复:] [quote=引用 2 楼 zyc13701469860 的回复:] 写了一个,最坏时间复杂度O(3n)

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

/**
 * 一个地方有两种人:诚实的人和骗子,诚实的人只说真话,骗子可能说真话也可能说假话,已知的是这里诚实的人比骗子要多,
 * 而且当地人知道这里其他的人是骗子还是诚实的人。
 * 你来到这里,要求找出所有的骗子,你可以问这里任何的人A关于另外个人B的问题“B是不是骗子?”。设计一个找出所有骗子的算法,但是时间复杂度是O(n)
 * 
 * 
 */
public class Test008 {

	public static void main(String[] args) {
		ArrayList<People> list = new ArrayList<People>();
		// init
		final int PEOPLE_COUNT = 20;
		final int SWINDLER_COUNT = Math.max(1,
				new Random().nextInt(PEOPLE_COUNT - 1) / 2);
		for (int i = 0; i < SWINDLER_COUNT; ++i) {
			list.add(new Swindler());
		}
		for (int i = 0; i < PEOPLE_COUNT - SWINDLER_COUNT; ++i) {
			list.add(new Honester());
		}
		//打乱顺序
		Collections.shuffle(list);
		//为方便查看结果,显示序号
		for(int i = 0;i < list.size();++i) {
			People p = list.get(i);
			p.setNum(i);
		}
		System.out.println("询问前");
		System.out.println(list);
		//获取骗子序号
		ArrayList<Integer> resultList = findSwindlers(list);
		System.out.println("询问后,骗子序号:");
		System.out.println(resultList);
	}

	/**
	 * 找到一个诚实的人就OK
	 * 第一次询问序号0 ~ N-1,检测第一个人是否是骗子(说他是骗子的人数 >= N/2)
	 * 如果不是骗子,就可以得到结果
	 * 否则从N/2 ~ N-1开始询问,检测第N/2个人是否是骗子(说他是骗子的人数 >= N/4)
	 * ...
	 * 以此类推,直到找出诚实的人
	 * 以上询问时间为N + N/2 + N/4 + N/8 + ... 无限接近O(2n)
	 * 最后询问诚实的人时间复杂度为O(n)
	 * 故总时间复杂度为O(3n)
	 */
	static ArrayList<Integer> findSwindlers(ArrayList<People> list) {
		boolean isHonester = false;
		int start = 0;
		int end = list.size();
		while(!isHonester) {
			int swindlerCount = 0;
			People checkPeople = list.get(start);
			for(int i = start;i < end;++i) {
				People p = list.get(i);
				if(p.say(checkPeople)) {
					++swindlerCount;
				}
			}
			if(swindlerCount >= (end - start) / 2) {
				//骗子
				start = (end + start) / 2;
			} else {
				isHonester = true;
			}
		}
		People honester = list.get(start);
		ArrayList<Integer> resultList = new ArrayList<Integer>();
		for(int i = 0;i < list.size();++i) {
			People p = list.get(i);
			if(honester.say(p)) {
				resultList.add(i);
			}
		}
		return resultList;
	}
}

abstract class People {
	protected boolean isSwindler = false;
	protected int num;//编号
	
	protected void setNum(int num) {
		this.num = num;
	}
	
	protected abstract boolean say(People p);
}

class Honester extends People {
	public Honester() {
		isSwindler = false;
	}

	@Override
	protected boolean say(People p) {
		return p.isSwindler;
	}

	@Override
	public String toString() {
		return num + "诚实";
	}
}

class Swindler extends People {
	public Swindler() {
		isSwindler = true;
	}

	@Override
	protected boolean say(People p) {
		int seed = new Random().nextInt(2);
		return seed == 0;
	}

	@Override
	public String toString() {
		return num + "骗子";
	}
}

上面写的有问题... 其实问题很简单,用消除法就可以做。 设诚实的人为H,骗子为S,H数量>S数量 每次依次从链表中选2人互相询问,会出现以下3种情况 H-H,都不是骗子,选1个放入链表继续,丢弃另一个 H-S,至少有1人会说对方是骗子,这种情况两个都丢弃 S-S,除非2人都说对方不是骗子,那么就选一个放入链表继续,丢弃另一个;否则两个都丢弃 直到队列长度为3 因为在最坏的情况下最后会出现 HHS,HSH,SHH 3种情形,其中第一种不能再消除了 所以只能再做一次判断 选前2人询问 如果都不是骗子,那么都是诚实的人,否则剩下的那个就是诚实的人 最后只要询问诚实的人就能得到结果 下面是findSwindlers方法的改写

static ArrayList<Integer> findSwindlers(LinkedList<People> list) {
    	People[] arr = new People[list.size()];
    	list.toArray(arr);
    	while(list.size() > 3) {
    		People p1 = list.remove();
    		People p2 = list.remove();
    		if(!p1.say(p2) && !p2.say(p1)) {
    			list.add(p1);
    		}
    	}
    	
    	People p1 = list.remove();
		People p2 = list.remove();
        People honester = p1.say(p2) && p2.say(p1) ? p1 : list.remove();
        ArrayList<Integer> resultList = new ArrayList<Integer>();
        for(int i = 0;i < arr.length;++i) {
            People p = arr[i];
            if(honester.say(p)) {
                resultList.add(i);
            }
        }
        return resultList;
    }
[/quote] 这个算法有问题吧,考虑5个元素: HHHSS的情况,循环结束后,队列里只剩2个元素了[/quote] 确实少考虑了这种情况 如果最后只剩下2个人,必定有一个诚实的人,只要再遍历一次全队列就可以找到。

People p1 = list.remove();
		People p2 = list.remove();
		People honester = null;
		if (list.size() == 3) {
			honester = !p1.say(p2) && !p2.say(p1) ? p1 : list.remove();
		} else {
			int count = 0;
			for (People people : arr) {
				if(people.say(p1)) ++count;
			}
			honester = count >= arr.length / 2 ? p2 : p1;
		}
lxgwm2008 2013-09-24
  • 打赏
  • 举报
回复
引用 3 楼 zyc13701469860 的回复:
[quote=引用 2 楼 zyc13701469860 的回复:] 写了一个,最坏时间复杂度O(3n)

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

/**
 * 一个地方有两种人:诚实的人和骗子,诚实的人只说真话,骗子可能说真话也可能说假话,已知的是这里诚实的人比骗子要多,
 * 而且当地人知道这里其他的人是骗子还是诚实的人。
 * 你来到这里,要求找出所有的骗子,你可以问这里任何的人A关于另外个人B的问题“B是不是骗子?”。设计一个找出所有骗子的算法,但是时间复杂度是O(n)
 * 
 * 
 */
public class Test008 {

	public static void main(String[] args) {
		ArrayList<People> list = new ArrayList<People>();
		// init
		final int PEOPLE_COUNT = 20;
		final int SWINDLER_COUNT = Math.max(1,
				new Random().nextInt(PEOPLE_COUNT - 1) / 2);
		for (int i = 0; i < SWINDLER_COUNT; ++i) {
			list.add(new Swindler());
		}
		for (int i = 0; i < PEOPLE_COUNT - SWINDLER_COUNT; ++i) {
			list.add(new Honester());
		}
		//打乱顺序
		Collections.shuffle(list);
		//为方便查看结果,显示序号
		for(int i = 0;i < list.size();++i) {
			People p = list.get(i);
			p.setNum(i);
		}
		System.out.println("询问前");
		System.out.println(list);
		//获取骗子序号
		ArrayList<Integer> resultList = findSwindlers(list);
		System.out.println("询问后,骗子序号:");
		System.out.println(resultList);
	}

	/**
	 * 找到一个诚实的人就OK
	 * 第一次询问序号0 ~ N-1,检测第一个人是否是骗子(说他是骗子的人数 >= N/2)
	 * 如果不是骗子,就可以得到结果
	 * 否则从N/2 ~ N-1开始询问,检测第N/2个人是否是骗子(说他是骗子的人数 >= N/4)
	 * ...
	 * 以此类推,直到找出诚实的人
	 * 以上询问时间为N + N/2 + N/4 + N/8 + ... 无限接近O(2n)
	 * 最后询问诚实的人时间复杂度为O(n)
	 * 故总时间复杂度为O(3n)
	 */
	static ArrayList<Integer> findSwindlers(ArrayList<People> list) {
		boolean isHonester = false;
		int start = 0;
		int end = list.size();
		while(!isHonester) {
			int swindlerCount = 0;
			People checkPeople = list.get(start);
			for(int i = start;i < end;++i) {
				People p = list.get(i);
				if(p.say(checkPeople)) {
					++swindlerCount;
				}
			}
			if(swindlerCount >= (end - start) / 2) {
				//骗子
				start = (end + start) / 2;
			} else {
				isHonester = true;
			}
		}
		People honester = list.get(start);
		ArrayList<Integer> resultList = new ArrayList<Integer>();
		for(int i = 0;i < list.size();++i) {
			People p = list.get(i);
			if(honester.say(p)) {
				resultList.add(i);
			}
		}
		return resultList;
	}
}

abstract class People {
	protected boolean isSwindler = false;
	protected int num;//编号
	
	protected void setNum(int num) {
		this.num = num;
	}
	
	protected abstract boolean say(People p);
}

class Honester extends People {
	public Honester() {
		isSwindler = false;
	}

	@Override
	protected boolean say(People p) {
		return p.isSwindler;
	}

	@Override
	public String toString() {
		return num + "诚实";
	}
}

class Swindler extends People {
	public Swindler() {
		isSwindler = true;
	}

	@Override
	protected boolean say(People p) {
		int seed = new Random().nextInt(2);
		return seed == 0;
	}

	@Override
	public String toString() {
		return num + "骗子";
	}
}

上面写的有问题... 其实问题很简单,用消除法就可以做。 设诚实的人为H,骗子为S,H数量>S数量 每次依次从链表中选2人互相询问,会出现以下3种情况 H-H,都不是骗子,选1个放入链表继续,丢弃另一个 H-S,至少有1人会说对方是骗子,这种情况两个都丢弃 S-S,除非2人都说对方不是骗子,那么就选一个放入链表继续,丢弃另一个;否则两个都丢弃 直到队列长度为3 因为在最坏的情况下最后会出现 HHS,HSH,SHH 3种情形,其中第一种不能再消除了 所以只能再做一次判断 选前2人询问 如果都不是骗子,那么都是诚实的人,否则剩下的那个就是诚实的人 最后只要询问诚实的人就能得到结果 下面是findSwindlers方法的改写

static ArrayList<Integer> findSwindlers(LinkedList<People> list) {
    	People[] arr = new People[list.size()];
    	list.toArray(arr);
    	while(list.size() > 3) {
    		People p1 = list.remove();
    		People p2 = list.remove();
    		if(!p1.say(p2) && !p2.say(p1)) {
    			list.add(p1);
    		}
    	}
    	
    	People p1 = list.remove();
		People p2 = list.remove();
        People honester = p1.say(p2) && p2.say(p1) ? p1 : list.remove();
        ArrayList<Integer> resultList = new ArrayList<Integer>();
        for(int i = 0;i < arr.length;++i) {
            People p = arr[i];
            if(honester.say(p)) {
                resultList.add(i);
            }
        }
        return resultList;
    }
[/quote] 这个算法有问题吧,考虑5个元素: HHHSS的情况,循环结束后,队列里只剩2个元素了
lxgwm2008 2013-09-24
  • 打赏
  • 举报
回复
引用 13 楼 lxgwm2008 的回复:
[quote=引用 11 楼 zyc13701469860 的回复:] [quote=引用 8 楼 zoeg 的回复:] [quote=引用 6 楼 zyc13701469860 的回复:] [quote=引用 5 楼 lxgwm2008 的回复:] [quote=引用 4 楼 zyc13701469860 的回复:] 上面最后3个人的判断条件应该是

 People honester = (!p1.say(p2) && !p2.say(p1)) ? p1 : list.remove();
写错了 汗
有个地方不懂,请教一下: 最后三个元素,为什么说最坏有三种情况HHS,HSH,SHH?应该还有其他4种情况HSS,SHS,SSH,HHH?这四种情况如何判断?[/quote] 因为每次循环至少可以消除等量的H-S,一般情况下会消除更多的S,比如S-S,由于H比S多,所以不会出现HSS,SHS,SSH,SSS这几种情况 HHH情形同HHS[/quote] 不知是我理解能力不行还是确实有问题,你思路中的丢弃是随意的,所以这个等量的前提应该站不住脚![/quote] 丢弃是有条件的 H-H必消除1个H H-S必消除一个H和1个S(H必说S是骗子) S-S可能消除1个S(都说对方不是骗子,25%),也可能消除2个S(只要有1人说对方是骗子,75%) 所以遍历一轮之后,消去的S至少和H相同,那么最后H肯定比S多。[/quote] 发现这个复杂度不是O(n)哦,应该是O(n^2)[/quote] 我错了,是O(n)
lxgwm2008 2013-09-24
  • 打赏
  • 举报
回复
引用 11 楼 zyc13701469860 的回复:
[quote=引用 8 楼 zoeg 的回复:] [quote=引用 6 楼 zyc13701469860 的回复:] [quote=引用 5 楼 lxgwm2008 的回复:] [quote=引用 4 楼 zyc13701469860 的回复:] 上面最后3个人的判断条件应该是

 People honester = (!p1.say(p2) && !p2.say(p1)) ? p1 : list.remove();
写错了 汗
有个地方不懂,请教一下: 最后三个元素,为什么说最坏有三种情况HHS,HSH,SHH?应该还有其他4种情况HSS,SHS,SSH,HHH?这四种情况如何判断?[/quote] 因为每次循环至少可以消除等量的H-S,一般情况下会消除更多的S,比如S-S,由于H比S多,所以不会出现HSS,SHS,SSH,SSS这几种情况 HHH情形同HHS[/quote] 不知是我理解能力不行还是确实有问题,你思路中的丢弃是随意的,所以这个等量的前提应该站不住脚![/quote] 丢弃是有条件的 H-H必消除1个H H-S必消除一个H和1个S(H必说S是骗子) S-S可能消除1个S(都说对方不是骗子,25%),也可能消除2个S(只要有1人说对方是骗子,75%) 所以遍历一轮之后,消去的S至少和H相同,那么最后H肯定比S多。[/quote] 发现这个复杂度不是O(n)哦,应该是O(n^2)
Mourinho 2013-09-24
  • 打赏
  • 举报
回复
引用 7 楼 zyb134506 的回复:
用多数原则,先问A是不是骗子,其中少数的答案全为骗子,多数答案可以知道A是否是骗子. 去除骗子集合,然后继续X是不是骗子,直至过滤到答案全部一致时. 然后历遍每个成员检查其是否为骗子,最后结束.
最坏情况下,时间复杂度是O(n^2)
Mourinho 2013-09-24
  • 打赏
  • 举报
回复 1
引用 8 楼 zoeg 的回复:
[quote=引用 6 楼 zyc13701469860 的回复:] [quote=引用 5 楼 lxgwm2008 的回复:] [quote=引用 4 楼 zyc13701469860 的回复:] 上面最后3个人的判断条件应该是

 People honester = (!p1.say(p2) && !p2.say(p1)) ? p1 : list.remove();
写错了 汗
有个地方不懂,请教一下: 最后三个元素,为什么说最坏有三种情况HHS,HSH,SHH?应该还有其他4种情况HSS,SHS,SSH,HHH?这四种情况如何判断?[/quote] 因为每次循环至少可以消除等量的H-S,一般情况下会消除更多的S,比如S-S,由于H比S多,所以不会出现HSS,SHS,SSH,SSS这几种情况 HHH情形同HHS[/quote] 不知是我理解能力不行还是确实有问题,你思路中的丢弃是随意的,所以这个等量的前提应该站不住脚![/quote] 丢弃是有条件的 H-H必消除1个H H-S必消除一个H和1个S(H必说S是骗子) S-S可能消除1个S(都说对方不是骗子,25%),也可能消除2个S(只要有1人说对方是骗子,75%) 所以遍历一轮之后,消去的S至少和H相同,那么最后H肯定比S多。
zoeg 2013-09-24
  • 打赏
  • 举报
回复
楼上凭什么把两个骗子都剔除,他们可以都撒谎
Mourinho 2013-09-24
  • 打赏
  • 举报
回复
引用 19 楼 zyc13701469860 的回复:
[quote=引用 18 楼 zoeg 的回复:] HHHHHHSSSSS SHHHSS HHSS HSS 假设骗子都很狡猾!
上述代码确实有个问题,不过君子总比骗子多这个条件必须要有。 遍历的轮次相对与第一个人的位置。 这些人排成队依次检查,遇到骗子剔除队列,否则放到队列末尾,即约瑟夫环。 所以说在第二轮遍历之前,S始终不会比H多。 比如(我标上序号) 第一轮 [0诚实, 1诚实, 2诚实, 3诚实, 4诚实, 5诚实, 6骗子, 7骗子, 8骗子, 9骗子, 10骗子] [2诚实, 3诚实, 4诚实, 5诚实, 6骗子, 7骗子, 8骗子, 9骗子, 10骗子, 0诚实] [4诚实, 5诚实, 6骗子, 7骗子, 8骗子, 9骗子, 10骗子, 0诚实, 2诚实] [6骗子, 7骗子, 8骗子, 9骗子, 10骗子, 0诚实, 2诚实, 4诚实] 注意,这时第一轮遍历尚未结束,继续 [8骗子, 9骗子, 10骗子, 0诚实, 2诚实, 4诚实, 6骗子] [10骗子, 0诚实, 2诚实, 4诚实, 6骗子, 8骗子] 第一轮结束,“10骗子”在队首或队尾对结果不造成影响 第二轮 [2诚实, 4诚实, 6骗子, 8骗子] [6骗子, 8骗子, 2诚实] [2诚实, 6骗子] 最后应该只会剩下1个或2个人,上面提到的3个人的情况可以进一步简化到这里 前提是H必须比S多,否则剩下的就是S 1个人必然是H 2个人(SH,HH)再遍历下也可得出结果 上面的方法再改进下

		People honester = null;
		if (list.size() == 2) {
			People p1 = list.remove();
			People p2 = list.remove();
			int count = 0;
			for (People people : arr) {
				if(people.say(p1)) ++count;
			}
			honester = count >= arr.length / 2 ? p2 : p1;
		} else {
			honester = list.remove();
		}
[/quote] 方法前面也有改动,还是把整个方法贴出来

static ArrayList<Integer> findSwindlers(LinkedList<People> list) {
		People[] arr = new People[list.size()];
		list.toArray(arr);
		while (list.size() > 2) {
			People p1 = list.remove();
			People p2 = list.remove();
			if (!p1.say(p2) && !p2.say(p1)) {
				list.add(p1);
			}
			System.out.println(list);
		}
		
		People honester = null;
		if (list.size() == 2) {
			People p1 = list.remove();
			People p2 = list.remove();
			int count = 0;
			for (People people : arr) {
				if(people.say(p1)) ++count;
			}
			honester = count >= arr.length / 2 ? p2 : p1;
		} else {
			honester = list.remove();
		}
		ArrayList<Integer> resultList = new ArrayList<Integer>();
		for (int i = 0; i < arr.length; ++i) {
			People p = arr[i];
			if (honester.say(p)) {
				resultList.add(i);
			}
		}
		return resultList;
	}
}
Mourinho 2013-09-24
  • 打赏
  • 举报
回复
引用 18 楼 zoeg 的回复:
HHHHHHSSSSS SHHHSS HHSS HSS 假设骗子都很狡猾!
上述代码确实有个问题,不过君子总比骗子多这个条件必须要有。 遍历的轮次相对与第一个人的位置。 这些人排成队依次检查,遇到骗子剔除队列,否则放到队列末尾,即约瑟夫环。 所以说在第二轮遍历之前,S始终不会比H多。 比如(我标上序号) 第一轮 [0诚实, 1诚实, 2诚实, 3诚实, 4诚实, 5诚实, 6骗子, 7骗子, 8骗子, 9骗子, 10骗子] [2诚实, 3诚实, 4诚实, 5诚实, 6骗子, 7骗子, 8骗子, 9骗子, 10骗子, 0诚实] [4诚实, 5诚实, 6骗子, 7骗子, 8骗子, 9骗子, 10骗子, 0诚实, 2诚实] [6骗子, 7骗子, 8骗子, 9骗子, 10骗子, 0诚实, 2诚实, 4诚实] 注意,这时第一轮遍历尚未结束,继续 [8骗子, 9骗子, 10骗子, 0诚实, 2诚实, 4诚实, 6骗子] [10骗子, 0诚实, 2诚实, 4诚实, 6骗子, 8骗子] 第一轮结束,“10骗子”在队首或队尾对结果不造成影响 第二轮 [2诚实, 4诚实, 6骗子, 8骗子] [6骗子, 8骗子, 2诚实] [2诚实, 6骗子] 最后应该只会剩下1个或2个人,上面提到的3个人的情况可以进一步简化到这里 前提是H必须比S多,否则剩下的就是S 1个人必然是H 2个人(SH,HH)再遍历下也可得出结果 上面的方法再改进下

		People honester = null;
		if (list.size() == 2) {
			People p1 = list.remove();
			People p2 = list.remove();
			int count = 0;
			for (People people : arr) {
				if(people.say(p1)) ++count;
			}
			honester = count >= arr.length / 2 ? p2 : p1;
		} else {
			honester = list.remove();
		}
zoeg 2013-09-24
  • 打赏
  • 举报
回复
HHHHHHSSSSS SHHHSS HHSS HSS 假设骗子都很狡猾!
zoeg 2013-09-24
  • 打赏
  • 举报
回复
zyc13701469860 针对你的思路我有一个疑问想请教: 假设初始队列是HHHHSSSSH,执行了两次判断后应该是前面的4个H被干掉了2个剩两个跟在后面,变成SSSSHHH; 但是,以我的理解,我觉得到这一步并没有实质性的进展(我的意思是如果输入序列一开始就是SSSSHH对这个算法而言没有区别),但是你能够继续从SSSSHHH中得出结论,那么说明骗子比君子多这个问题也是有解的了,那么题目中所给出的条件:君子总比骗子多似乎显得有些多余。 我为了举例是为了方便说明,如果排在前面的一堆都是君子,那么算法的最初应该会干掉大部分的君子,那么君子可能比骗子少,虽然我没有办法直接举出反证证明你的思路有问题,我只能看到这个角度所表现出的可能存在的错误!
lxgwm2008 2013-09-23
  • 打赏
  • 举报
回复
引用 4 楼 zyc13701469860 的回复:
上面最后3个人的判断条件应该是

 People honester = (!p1.say(p2) && !p2.say(p1)) ? p1 : list.remove();
写错了 汗
有个地方不懂,请教一下: 最后三个元素,为什么说最坏有三种情况HHS,HSH,SHH?应该还有其他4种情况HSS,SHS,SSH,HHH?这四种情况如何判断?
zoeg 2013-09-23
  • 打赏
  • 举报
回复
我居然把它做完了,离职了所以闲的蛋疼,数学不好,不要让我求算法复杂度!

package test;

import java.security.SecureRandom;
import java.util.ArrayList;

public class TrustAndLie{
  private static SecureRandom sr=new SecureRandom();

  public static void main(String...args){
    Group unknown=Group.generateSource();
    Group lieGroup=findLieGroup(unknown);
    unknown.removeAll(lieGroup);
    lieGroup.assertLie();
    unknown.assertTrust();
    System.out.println("I got it, Lie count:"+lieGroup.size()+"   Trust count:"+unknown.size());
  }

  private static Group findLieGroup(Group unknown){
    Group sayTrust=new Group();
    Group sayLie=new Group();
    Group lieGroup=new Group();
    while(unknown.size()>0){
      People base=unknown.remove(0);
      for(People people:unknown)
        if(people.test(base)) sayTrust.add(people);
        else sayLie.add(people);
      if(sayLie.size()==sayTrust.size()) return sayLie;
      if(sayTrust.size()>sayLie.size()){
        lieGroup.addAll(sayLie);
        for(People people:sayTrust)
          if(!base.test(people)) lieGroup.add(people);
        return lieGroup;
      }
      lieGroup.add(base);
      lieGroup.addAll(sayTrust);
      unknown.removeAll(sayTrust);
      sayTrust.clear();
      sayLie.clear();
    }
    return lieGroup;
  }

  private static class Group extends ArrayList<People>{
    private static final long serialVersionUID=145658485844788783L;

    public static Group generateSource(){
      Group group=new Group();
      int trust=0;
      int lie=0;
      for(int i=0;i<1000;i++){
        if(sr.nextBoolean()){
          group.add(new Trust());
          trust++;
        }else{
          group.add(new Lie());
          lie++;
        }
      }
      for(int i=0;i<lie+1-trust;i++)
        group.add(new Trust());
      return group;
    }

    public void assertTrust(){
      for(People people:this)
        people.assertTrust();
    }

    public void assertLie(){
      for(People people:this)
        people.assertLie();
    }
  }

  private static class People{
    private boolean trust;

    public People(boolean trust){
      this.trust=trust;
    }

    public boolean test(People people){
      return people.trust;
    }

    public void assertTrust(){
    }

    public void assertLie(){
    }
  }

  private static class Trust extends People{
    public Trust(){
      super(true);
    }

    @Override
    public void assertLie(){
      throw new IllegalArgumentException("You're wrong!");
    }
  }

  private static class Lie extends People{
    public Lie(){
      super(false);
    }

    @Override
    public boolean test(People people){
      return sr.nextBoolean();
    }

    @Override
    public void assertTrust(){
      throw new IllegalArgumentException("You're wrong!");
    }
  }
}

lxgwm2008 2013-09-23
  • 打赏
  • 举报
回复
引用 6 楼 zyc13701469860 的回复:
[quote=引用 5 楼 lxgwm2008 的回复:] [quote=引用 4 楼 zyc13701469860 的回复:] 上面最后3个人的判断条件应该是

 People honester = (!p1.say(p2) && !p2.say(p1)) ? p1 : list.remove();
写错了 汗
有个地方不懂,请教一下: 最后三个元素,为什么说最坏有三种情况HHS,HSH,SHH?应该还有其他4种情况HSS,SHS,SSH,HHH?这四种情况如何判断?[/quote] 因为每次循环至少可以消除等量的H-S,一般情况下会消除更多的S,比如S-S,由于H比S多,所以不会出现HSS,SHS,SSH,SSS这几种情况 HHH情形同HHS[/quote]

if(!p1.say(p2) && !p2.say(p1)) {
    list.add(p1);
}
这块代码可能只消除H,不消除S
zoeg 2013-09-23
  • 打赏
  • 举报
回复
引用 6 楼 zyc13701469860 的回复:
[quote=引用 5 楼 lxgwm2008 的回复:] [quote=引用 4 楼 zyc13701469860 的回复:] 上面最后3个人的判断条件应该是

 People honester = (!p1.say(p2) && !p2.say(p1)) ? p1 : list.remove();
写错了 汗
有个地方不懂,请教一下: 最后三个元素,为什么说最坏有三种情况HHS,HSH,SHH?应该还有其他4种情况HSS,SHS,SSH,HHH?这四种情况如何判断?[/quote] 因为每次循环至少可以消除等量的H-S,一般情况下会消除更多的S,比如S-S,由于H比S多,所以不会出现HSS,SHS,SSH,SSS这几种情况 HHH情形同HHS[/quote] 不知是我理解能力不行还是确实有问题,你思路中的丢弃是随意的,所以这个等量的前提应该站不住脚!
快乐的2 2013-09-23
  • 打赏
  • 举报
回复
用多数原则,先问A是不是骗子,其中少数的答案全为骗子,多数答案可以知道A是否是骗子. 去除骗子集合,然后继续X是不是骗子,直至过滤到答案全部一致时. 然后历遍每个成员检查其是否为骗子,最后结束.
Mourinho 2013-09-23
  • 打赏
  • 举报
回复
引用 5 楼 lxgwm2008 的回复:
[quote=引用 4 楼 zyc13701469860 的回复:] 上面最后3个人的判断条件应该是

 People honester = (!p1.say(p2) && !p2.say(p1)) ? p1 : list.remove();
写错了 汗
有个地方不懂,请教一下: 最后三个元素,为什么说最坏有三种情况HHS,HSH,SHH?应该还有其他4种情况HSS,SHS,SSH,HHH?这四种情况如何判断?[/quote] 因为每次循环至少可以消除等量的H-S,一般情况下会消除更多的S,比如S-S,由于H比S多,所以不会出现HSS,SHS,SSH,SSS这几种情况 HHH情形同HHS
zhujun88nz 2013-09-22
  • 打赏
  • 举报
回复
求助求助求助求助
加载更多回复(3)

51,395

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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