UVA10603 Fill

13 篇文章 0 订阅
7 篇文章 0 订阅

题目链接:

UVA
luogu

题目描述:

有三个容量分别为a,b,c升的容器(a,b,c都是正整数,且都不超过200),刚开始的时候第一个和第二个杯子都是空的,只有第三个杯子装满了c升水。允许从一个容器把水倒入另一个容器中,直到一个容器空了或者是另一个容器满了,允许无限次的进行这样的倒水操作。

你的任务是编写一个程序来计算出最少需要倒多少升水才能让其中某一个杯子中的水有d升(d是不超过200的正整数)?如果无法做到恰好是d升,就让某一个杯子里的水是d‘升,其中d’<d并且尽量接近d。如果能够找到这样的d’,你还是需要计算出其中某一个杯子达到d’升时,最少需要倒多少升水。

题解:

将三个杯子分别装有x,y,z的水时记为状态f(x,y,z),对应与一个点,最多200^3个点,进行一次倒水之后会到达一个新的状态f(x’,y’,z’),从f(x,y,z)到f(x’,y’,z’)建一条有向边,边权为倒的水的数量,从每个状态出发最多有6条边。形成一个带权有向图。跑最短路即可。
因为水的总量不会改变,即x+y+z==c,所以可以只记录x,y,z=c-x-y,状态总数就只有200^2个,可以比上面快100倍。(三元状态建图也能过)

代码:

#include <bits/stdc++.h>
#define LL long long
#define LD long double
#define ULL unsigned long long
#define UI unsigned int
#define PII pair<int,int>
#define MPII(x,y) pair<int,int>{x,y}
#define _for(i,j,k) for(int i=j;i<=k;i++)
#define for_(i,j,k) for(int i=j;i>=k;i--)
#define efor(i,u) for(int i=head[u];i;i=net[i])
#define lowbit(x) (x&-x)
#define ls(x) x<<1
#define rs(x) x<<1|1
#define inf 0x3fffffff
//#pragma comment(linker, "/STACK:10240000000,10240000000")
using namespace std;
const int maxn = 8e6 + 5;
const int M = 1e9 + 7;
inline int mad(int a,int b){return (a+=b)>=M?a-M:a;}
struct E{
	int v,w;
}e[maxn<<1];
int id[201][201],vcnt;
int head[maxn],net[maxn<<1],cnt;
void add(int u,int v,int w){
	e[++cnt]=E{v,w};
	net[cnt]=head[u];
	head[u]=cnt;
} 
int T,a,b,c,d;
void dfs(int x,int y){
	if(id[x][y]) return;
	int u= id[x][y]=++vcnt;
	int dt,z=c-x-y;
	if(x&&y<b){//x->y
		dt=min(x,b-y);
		if(!id[x-dt][y+dt]) dfs(x-dt,y+dt);
		add(u,id[x-dt][y+dt],dt);
	}
	if(x&&z<c){//x->z
		dt=min(x,c-z);
		if(!id[x-dt][y]) dfs(x-dt,y);
		add(u,id[x-dt][y],dt);
	}
	if(y&&x<a){//y->x
		dt=min(y,a-x);
		if(!id[x+dt][y-dt]) dfs(x+dt,y-dt);
		add(u,id[x+dt][y-dt],dt);
	}
	if(y&&z<c){//y->z
		dt=min(y,c-z);
		if(!id[x][y-dt]) dfs(x,y-dt);
		add(u,id[x][y-dt],dt);
	}
	if(z&&x<a){//z->x
		dt=min(z,a-x);
		if(!id[x+dt][y]) dfs(x+dt,y);
		add(u,id[x+dt][y],dt);
	}
	if(z&&y<b){//z->y
		dt=min(z,b-y);
		if(!id[x][y+dt]) dfs(x,y+dt);
		add(u,id[x][y+dt],dt);
	}
}
int dis[maxn];
priority_queue<PII,vector<PII>,greater<PII> > q;
void dj(int u){
	_for(i,1,vcnt) dis[i]=inf;
	dis[u]=0;
	q.push(PII{0,u});
	int w;
	while(!q.empty()){
		u=q.top().second;
		w=q.top().first;
		q.pop();
		if(w>dis[u]) continue;
		for(int i=head[u];i;i=net[i]){
			if(dis[e[i].v]>dis[u]+e[i].w){
				dis[e[i].v]=dis[u]+e[i].w;
				q.push(PII{dis[e[i].v],e[i].v});
			}
		}
	}
}
int an[202];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--){
		cin>>a>>b>>c>>d;
		dfs(0,0);
		dj(1);
		_for(i,0,200) an[i]=inf;
		_for(x,0,a) _for(y,0,b) {
			int z=c-x-y;
			int u=id[x][y];
			if(u){
				an[x]=min(an[x],dis[u]);
				an[y]=min(an[y],dis[u]);
				an[z]=min(an[z],dis[u]);
			}
		}
		for_(i,d,0){
			if(an[i]!=inf){
				cout<<an[i]<<" "<<i<<"\n";
				break;
			}
		}
		_for(i,1,vcnt) head[i]=0;
		_for(x,0,a) _for(y,0,b) id[x][y]=0;
		vcnt=cnt=0;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值