You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
6.1 KiB

2 years ago
package class02;
public class Code02_Cola {
/*
* 3000MS 589824KB
*
* 退10 50100
* 100305010
* m 10,50,100 a,b,cx(x10)
* 使, m 10 a 50 b
* 100 c 1 x
* 22501003504101 1100350 2505
* 8 2 1 4 3 250 8
*/
// 暴力尝试,为了验证正式方法而已
public static int right(int m, int a, int b, int c, int x) {
int[] qian = { 100, 50, 10 };
int[] zhang = { c, b, a };
int puts = 0;
while (m != 0) {
int cur = buy(qian, zhang, x);
if (cur == -1) {
return -1;
}
puts += cur;
m--;
}
return puts;
}
public static int buy(int[] qian, int[] zhang, int rest) {
int first = -1;
for (int i = 0; i < 3; i++) {
if (zhang[i] != 0) {
first = i;
break;
}
}
if (first == -1) {
return -1;
}
if (qian[first] >= rest) {
zhang[first]--;
giveRest(qian, zhang, first + 1, qian[first] - rest, 1);
return 1;
} else {
zhang[first]--;
int next = buy(qian, zhang, rest - qian[first]);
if (next == -1) {
return -1;
}
return 1 + next;
}
}
// 正式的方法
// 要买的可乐数量m
// 100元有a张
// 50元有b张
// 10元有c张
// 可乐单价x
public static int putTimes(int m, int a, int b, int c, int x) {
// 0 1 2
int[] qian = { 100, 50, 10 };
int[] zhang = { c, b, a };
// 总共需要多少次投币
int puts = 0;
// 之前面值的钱还剩下多少总钱数
int preQianRest = 0;
// 之前面值的钱还剩下多少总张数
int preQianZhang = 0;
for (int i = 0; i < 3 && m != 0; i++) {
// 要用之前剩下的钱、当前面值的钱,共同买第一瓶可乐
// 之前的面值剩下多少钱是preQianRest
// 之前的面值剩下多少张是preQianZhang
// 之所以之前的面值会剩下来,一定是剩下的钱,一直攒不出一瓶可乐的单价
// 当前的面值付出一些钱+之前剩下的钱,此时有可能凑出一瓶可乐来
// 那么当前面值参与搞定第一瓶可乐需要掏出多少张呢就是curQianFirstBuyZhang
int curQianFirstBuyZhang = (x - preQianRest + qian[i] - 1) / qian[i];
if (zhang[i] >= curQianFirstBuyZhang) { // 如果之前的钱和当前面值的钱,能凑出第一瓶可乐
// 凑出来了一瓶可乐也可能存在找钱的情况,
giveRest(qian, zhang, i + 1, (preQianRest + qian[i] * curQianFirstBuyZhang) - x, 1);
puts += curQianFirstBuyZhang + preQianZhang;
zhang[i] -= curQianFirstBuyZhang;
m--;
} else { // 如果之前的钱和当前面值的钱,不能凑出第一瓶可乐
preQianRest += qian[i] * zhang[i];
preQianZhang += zhang[i];
continue;
}
// 凑出第一瓶可乐之后,当前的面值有可能能继续买更多的可乐
// 以下过程就是后续的可乐怎么用当前面值的钱来买
// 用当前面值的钱,买一瓶可乐需要几张
int curQianBuyOneColaZhang = (x + qian[i] - 1) / qian[i];
// 用当前面值的钱,一共可以搞定几瓶可乐
int curQianBuyColas = Math.min(zhang[i] / curQianBuyOneColaZhang, m);
// 用当前面值的钱,每搞定一瓶可乐,收货机会吐出多少零钱
int oneTimeRest = qian[i] * curQianBuyOneColaZhang - x;
// 每次买一瓶可乐吐出的找零总钱数是oneTimeRest
// 一共买的可乐数是curQianBuyColas所以把零钱去提升后面几种面值的硬币数
// 就是giveRest的含义
giveRest(qian, zhang, i + 1, oneTimeRest, curQianBuyColas);
// 当前面值去搞定可乐这件事,一共投了几次币
puts += curQianBuyOneColaZhang * curQianBuyColas;
// 还剩下多少瓶可乐需要去搞定,继续用后面的面值搞定去吧
m -= curQianBuyColas;
// 当前面值可能剩下若干张,要参与到后续买可乐的过程中去,
// 所以要更新preQianRest和preQianZhang
zhang[i] -= curQianBuyOneColaZhang * curQianBuyColas;
preQianRest = qian[i] * zhang[i];
preQianZhang = zhang[i];
}
return m == 0 ? puts : -1;
}
public static void giveRest(int[] qian, int[] zhang, int i, int oneTimeRest, int times) {
for (; i < 3; i++) {
zhang[i] += (oneTimeRest / qian[i]) * times;
oneTimeRest %= qian[i];
}
}
public static void main(String[] args) {
int testTime = 1000;
int zhangMax = 10;
int colaMax = 10;
int priceMax = 20;
System.out.println("如果错误会打印错误数据,否则就是正确");
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int m = (int) (Math.random() * colaMax);
int a = (int) (Math.random() * zhangMax);
int b = (int) (Math.random() * zhangMax);
int c = (int) (Math.random() * zhangMax);
int x = ((int) (Math.random() * priceMax) + 1) * 10;
int ans1 = putTimes(m, a, b, c, x);
int ans2 = right(m, a, b, c, x);
if (ans1 != ans2) {
System.out.println("int m = " + m + ";");
System.out.println("int a = " + a + ";");
System.out.println("int b = " + b + ";");
System.out.println("int c = " + c + ";");
System.out.println("int x = " + x + ";");
break;
}
}
System.out.println("test end");
}
}