倒水问题
问题描述
邓老师有有 2 个容量分别为 n 单位、m 单位的没有刻度的杯子。初始,它们都是空的。邓老师给了你 t 分钟时间。每一分钟,他都可以做下面 4 件事中的任意一件:
- 用水龙头装满一个杯子。
- 倒空一个杯子。
- 把一个杯子里的水倒到另一个杯子里,直到一个杯子空了或者另一个杯子满了。
- 什么都不做。
邓老师希望最后能获得 d 个单位的水,假设最后两个杯具中水量的总和为 x,那么邓老师的不满意度就为 |d-x|。
你希望邓老师尽可能地满意,于是请你计算邓老师的不满意度最小是多少。
输入格式
一行 4 个整数 n,m,t,d,分别表示两个杯具的容量、时间限制、以及邓老师的期望值。
输出格式
一行一个整数,表示邓老师最小的不满意度。
样例输入
7 25 2 16
样例输出
9
样例解释
你可以在第 1 分钟用水龙头装满任意一个杯子,并在第 2 分钟什么都不做,即可让邓老师的不满意度为 9。可以证明不存在更优的解。
代码实现
#include <bits/stdc++.h>
using namespace std;
// ================= 代码实现开始 =================
/* 请在这里定义你需要的全局变量 */
typedef pair<int,int> pii;
const int N = 2003;
// mind: mind[i][j]表示从初始状态到(i,j)需要的最少步数
// q: 模拟队列
// qh: 队头下标
// qt: 队尾下标
int mind[N][N];
pii q[N*N];
int qh, qt;
// 模拟倒水操作:
// p:当前状态
// k:操作码
// n:A杯容量
// m:B杯容量
// 返回值:操作之后的状态
pii to(pii p, int k, int n, int m){
switch(k){
case 0://倒空A
return pii(0, p.second);
case 1://倒空B
return pii(p.first, 0);
case 2://倒满A
return pii(n, p.second);
case 3://倒满B
return pii(p.second, m);
case 4://B倒入A:现有水量all=p.first+p.second, 若n<all, 则A杯必满, B杯=all-n; 若all<n, 则B杯必空, A杯=all
return pii(min(p.first + p.second, n), max(p.first + p.second - n, 0));
case 5://A倒入B:上述反向情况
return pii(max(p.first + p.second - n, 0), min(p.first + p.second, n));
default://无操作
return p;
}
}
// 计算答案的函数
// n, m, t, d:意义均与题目描述一致
// 返回值:即为答案
int getAnswer(int n, int m, int t, int d) {
/* 请在这里设计你的算法 */
memset(mind, -1, sizeof(mind));
qh = qt = 0;
q[++qt] = pii(0,0);//初始状态
mind[0][0] = 0;
// BFS
while(qh < qt){
pii u = q[++qh];
if(mind[u.first][u.second] == t)
break;//已经进行t步
for(int k=0; k<6; ++k){//忽略无操作
pii v = to(u, k, n, m);
if(mind[v.first][v.second] != -1)
continue;//判断该状态是否已经到达过
q[++qt] = v;
mind[v.first][v.second] = mind[u.first][u.second] + 1;//v是由u前进1步达到的状态
}
}
int ans = d;
for(int i=0; i<=n; ++i)
for(int j=0; j<=m; ++j)
if(mind[i][j] != -1)
ans = min(ans, mind[i][j]);
return ans;
}
// ================= 代码实现结束 =================
int main() {
int n, m, t, d;
scanf("%d%d%d%d", &n, &m, &t, &d);
int ans = getAnswer(n, m, t, d);
printf("%d\n", ans);
return 0;
}
来源
来自邓俊辉老师的《算法训练营》第一期。