1≤n≤2^30,输入0结束程序。
最多要称几次一定能把那个假币找出来?
3 12 0
1 3
#include <stdio.h>
#include <math.h>
int main()
{
unsigned n;
while (~scanf("%u", &n)) {
if (n==0) break;
int result = (int)ceil(log(n) / log(3));//ceil是进一法取整
printf("%d\n", result);
}
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()){
int n = sc.nextInt();
if(n == 0) break;
int count = 0;
while (n >= 2){
//Math.ceil向上取整,但是是double类型
n = (int)Math.ceil((double)n/3);
count++;
}
System.out.println(count);
}
}
}
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
int n;
while(cin >> n){
if(n == 0){
continue;
}
int count = 0;
while(n >= 2){
++count;
if(n % 3){
//不可以整除则取最差情况:特别的一份是 n/3 + 1个金币
n = n / 3 + 1;
}
else{
//可以整除在直接整除,能够获取到一份
n /= 3;
}
}
cout << count << endl;
}
return 0;
}
一看到这道题,我首先想到的是一个猜数游戏,就是随机生成[1,1000]之间的一个数,你每次报错一个猜想值,只告诉你你猜的数是比随机数大还是小,每次根据反馈调整猜的数值,直到猜中为止。玩过的都知道,折半查找是最快的,比如将[1,1000]分为[1,500]、[500,1000],第一次猜500,如果大了就猜250,否则猜750。这样按照折半的规则猜,最多需要 log2n向上取整 次。
对于这道题,我已开始的思路也是折半查找,把n分成两堆,如果n不是偶数,就分成(n - 1) / 2。但这种思路并不是最优的,无法通过所有测试。
我们首先来看前面几个示例:
当n = 1时,不需要再称了,它就是假币,总共需要0次
当n = 2时,1、1放天平两端,轻的就是假币,总共需要1次
当n = 3时,随机抽出2个放到天平两端,如果天平平衡,则剩下1个就是假币,
否则天平中较轻的是假币,总共需要1次
当n = 4时,分成1、1、2,天平秤1、1,注意题目要求最短时间,
并且是次数最大的情况,也就是我们需要考虑最坏的情况,第一次1、1重量相等,
接着我们把2分开称,总共需要2次
当n = 5时,分成2、2、1,天平秤2、2,同样考虑最坏的情况,2、2重量相等,
接着我们把2分开称,总共需要2次
当n = 6时,分成2、2、2,天平秤2、2,同样考虑最坏的情况,不管如何,还需要
把2分开称,总共需要2次
当n = 7时,分成2、2、3,天平先称2、2,考虑最坏的情况,重量相等,接着我们就需要
按照n = 3的最优情况称,总共需要2次
...
其中有一个规则,我们每次把n分成是3堆,
如果n % 3 == 0,分成 n/3、 n/3、 n/3三堆, 剩下 n/3
如果n % 3 == 1,分成 n/3、 n/3、1 + (n/3)三堆,最坏剩下 1 + (n/3)
如果n % 3 == 2,分成 n/3、 1 + (n/3)、1 + (n/3)三堆,最坏剩下 1 + (n/3)
#include <iostream>
using namespace std;
int main() {
int n = 0;
//scanf返回值为正确输入数据的变量个数,当一个变量都没有成功获取数据时,此时返回-1
while (scanf("%d", &n) != - 1) {
if (n == 0) {
break;
}
int count = 0;
while (n > 1) {
count += 1;
//每次取1/3,如果不能整除3,有两种情况
//剩余1个,分成 1/3 、1/3 、1 + (1/3) ,两个1/3放入天平两端,
//剩余2个,分成 1/3 、1 + (1/3) 、 1 + (1/3),两个1 + (1/3)放入天平两端
//由于题目要求最快的时间,并且是求最多的次数,因此取每次剩余的最大值 1 + (1/3)
n = n / 3 + (n % 3 > 0);
}
printf("%d\n", count);
}
return 0;
}
————————————————
版权声明:本文为CSDN博主「hestyle」的原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://hestyle.blog.csdn.net/article/details/104689962
#include <iostream>
using namespace std;
int main()
{
int n = 0;
while(cin >> n && n != 0)
{
//因为天平只有两端,所以至多可以均分为三堆
//假若不可均分为三堆,则优先分为 2n + m 的模式,其中 m 要尽可能的小
int num = 0;
while(n > 1)
{
if(n % 3 == 0)
n /= 3;
else
n = ((n / 3) + 1); //寻求最优解
num++;
}
cout << num << endl;
}
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextInt()) {
int n = in.nextInt();
if(n == 0){
return;
}else if(n == 1){
System.out.println(0);
}else if(n == 2 || n == 3){
System.out.println(1);
}else {
int count = 1;
while(n > 3){
if(n % 3 == 0){
n /= 3;
}else{
n = n / 3 +1;
}
count++;
}
System.out.println(count);
}
}
}
} def find(a):
对于 1个硬币,称量 0次
对于 2个硬币,称量 1次
对于 3个硬币,称量 1次
对于 4个硬币,称量 2次,先分成(2,2,0),第一次称量前两份(2,2),如果重量不一样,再次求出判断另外2个硬币需要称量的次数。
对于 5个硬币,称量 2次,先分成(2,2,1),第一次称量前两份(2,2),如果重量不一样,再次判断另外1个硬币需要称量的次数。
对于 6个硬币,称量 2次,先分成(2,2,2),第一次称量前两份(2,2),如果重量不一样,再次判断求出另外2个硬币需要称量的次数。
对于 7个硬币,称量 2次,先分成(3,3,1),第一次称量前两份(3,3),如果重量不一样,再次判断求出另外3个硬币需要称量的次数。
通过上面分析可以看出,对于要称量的硬币,每次称量前分成3份,要求前两份的个数不小于第三份。如果前两份重量是一样,那么假币在第三份中,这样就除去了2/3的硬币。
如果前两份重量不一样,那么假币在重量轻的一份中,这样也除去了2/3的硬币。
这样以来,称量一次除去了将近2/3的硬币,一直重复上面的分法,就可以很快求出称量次数。
import java.io.PrintStream; import java.util.Scanner; public class Main { public static Scanner in = new Scanner(System.in); public static PrintStream out = System.out; public static int findCoin(int n){ if(n==1) return 0; if(n<=3) return 1; int metage,rest,times=0; // 3等分前,先加2,使得metage的值尽量大于rest // (metage,metage,rest) metage = (n+2)/3; rest = n-2*metage; times= 1 + findCoin(Math.max(metage, rest)); return times; } public static void main(String[] args) { int n; while((n=in.nextInt()) > 0){ out.println(findCoin(n)); } } }