AcWing 1355. 母亲的牛奶(每日一题)

目录

题目:

 解题思路:

DFS版:

BFS版:

总结:


原题链接1355. 母亲的牛奶 - AcWing题库

题目:

农夫约翰有三个容量分别为 A,B,C 升的挤奶桶。

最开始桶 A和桶 B都是空的,而桶 C里装满了牛奶。

有时,约翰会将牛奶从一个桶倒到另一个桶中,直到被倒入牛奶的桶满了或者倒出牛奶的桶空了为止。

这一过程中间不能有任何停顿,并且不会有任何牛奶的浪费。

请你编写一个程序判断,当 A桶是空的时候,C桶中可能包含多少升牛奶,找出所有的可能情况。

输入格式

共一行,包含三个整数 A,B,C。

输出格式

共一行,包含若干个整数,表示 C桶中牛奶存量的所有可能情况,请将这些数字按升序排列。

数据范围

1≤A,B,C≤20

输入样例1:

8 9 10

输出样例1:

1 2 8 9 10

输入样例2:

2 5 10

输出样例2:

5 6 7 8 9 10
难度:中等
时/空限制:1s / 64MB
总通过数:2387
总尝试数:3215
来源:

usaco training 1.5

算法标签

BFS DFS


 解题思路:

此题主要是利用DFS、BFS搜索,题目数据量很小,时间复杂度为O(ABC),DFS、BFS都可以过。一个状态(a,b,c)可以有6种方案,可以a往b倒、a往c倒、b往a倒、b往c倒、c往a倒、c往b倒,用搜索把六种状态都都搜索一遍,用vis三维数组标记是否被搜过,最后再找一下A桶为0,C桶的体积即可。

这里解释一下样例,开始我也没看懂什么意思,c->a表示C桶往A桶倒每一个状态都可以向下转移搜索,红色框表示符合题意A=0。


DFS版:
#include<iostream>
using namespace std;
const int N = 25;
int A,B,C;
bool vis[N][N][N];
void dfs(int a,int b,int c){//abc表示此时三个桶牛奶的体积
    if(vis[a][b][c])return;//重复搜索
    vis[a][b][c]=true;
    if(a>B-b)dfs(a-B+b,B,c);//a往b里面倒
    else dfs(0,a+b,c);
    if(a>C-c)dfs(a-C+c,b,C);//a往c里面倒
    else dfs(0,b,a+c);
    if(b>A-a)dfs(A,b-A+a,c);//b往a里面倒
    else dfs(a+b,0,c);
    if(b>C-c)dfs(a,b-C+c,C);//b往c里面倒
    else dfs(a,0,b+c);
    if(c>A-a)dfs(A,b,c-A+a);//c往a里面倒
    else dfs(a+c,b,0);
    if(c>B-b)dfs(a,B,c-B+b);//c往b里面倒
    else dfs(a,b+c,0);
}
int main(){
    cin>>A>>B>>C;
    dfs(0,0,C);
    for(int i=0;i<=C;i++){//升序输出,先枚举C桶
        for(int j=0;j<=B;j++){
            if(vis[0][j][i]){
                cout<<i<<" ";
                break;
            }
        }
    }
    return 0;
}

BFS版:
#include<iostream>
#include<queue>
using namespace std;
const int N=25;
int A,B,C;
bool vis[N][N][N];
struct node{
	int a,b,c;
};
queue<node> q;
void insert(int a,int b,int c){
	if(!vis[a][b][c]){
		q.push({a,b,c});
		vis[a][b][c]=true;
	}
}
void bfs(){
	while(q.size()){
		auto t=q.front();
		q.pop();
		int x=t.a,y=t.b,z=t.c;//把六种情况枚举一遍
		insert(x-min(x,B-y),min(B,x+y),z);//a往b里面倒
		insert(x-min(x,C-z),y,min(C,x+z));//a往c里面倒
		insert(min(A,x+y),y-min(y,A-x),z);//b往a里面倒
		insert(x,y-min(y,C-z),min(y+z,C));//b往c里面倒
		insert(min(x+z,A),y,z-min(z,A-x));//c往a里面倒
		insert(x,min(B,y+z),z-min(z,B-y));//c往b里面倒
	}
}
int main(){
	cin>>A>>B>>C;
	q.push({0,0,C});
	vis[0][0][C]=true;
	bfs();
	for(int i=0;i<=C;i++){
		for(int j=0;j<=B;j++){
			if(vis[0][j][i]){
				cout<<i<<" ";
				break;
			}
		}
	}
	return 0;
}

总结:

主要考察DFS、BFS。记住板子,把自己想得思路往上写即可。此题最难的地方就是一个状态的六种转移方式。只要弄明白了六种转移方式,此题就迎刃而解了。文章尚有不足,若有错误地地方请大家指出,一起进步。

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
题目链接:https://www.acwing.com/problem/content/4948/ 题目描述: 给定一棵有 $n$ 个结点的树,结点从 $1$ 到 $n$ 编号,每个结点都有一个权值 $w_i$,现在有 $m$ 次操作,每次操作是将树中编号为 $x$ 的结点的权值加上 $y$,然后询问一些节点是否为叶子节点,如果是输出 $1$,否则输出 $0$。 输入格式: 第一行包含两个整数 $n$ 和 $m$。 第二行包含 $n$ 个整数,其中第 $i$ 个整数表示结点 $i$ 的初始权值 $w_i$。 接下来 $n-1$ 行,每行包含两个整数 $a$ 和 $b$,表示点 $a$ 和点 $b$ 之间有一条无向边。 接下来 $m$ 行,每行描述一次操作,格式为三个整数 $t,x,y$。其中 $t$ 表示操作类型,$t=1$ 时表示将编号为 $x$ 的结点的权值加上 $y$,$t=2$ 时表示询问编号为 $x$ 的结点是否为叶子节点。 输出格式: 对于每个操作 $t=2$,输出一个结果,表示询问的结点是否为叶子节点。 数据范围: $1≤n,m≤10^5$, $1≤w_i,y≤10^9$ 样例: 输入: 5 5 1 2 3 4 5 1 2 1 3 3 4 3 5 2 3 0 1 3 100 2 3 0 1 1 100 2 3 0 输出: 1 0 0 算法1: 暴力dfs,每次都重新遍历整棵树,时间复杂度 $O(nm)$ 时间复杂度: 最坏情况下,每次操作都要遍历整棵树,时间复杂度 $O(nm)$,无法通过此题。 算法2: 用一个 vector<int> sons[n+5] 来存储每个点的所有子节点,这样可以用 $O(n)$ 预处理出每个点的度数 $deg_i$,如果 $deg_i=0$,则 $i$ 是叶子节点,否则不是。 对于每个操作,只需要更新叶子节点关系的变化就可以了。如果某个节点的度数从 $1$ 变成 $0$,则该节点变成了叶子节点;如果某个节点的度数从 $0$ 变成 $1$,则该节点不再是叶子节点。 时间复杂度: 每次操作的时间复杂度是 $O(1)$,总时间复杂度 $O(m)$,可以通过此题。 C++ 代码: (算法2)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摆烂小白敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值