1. 程式人生 > 其它 >P2744 「USACO5.3」量取牛奶 Milk Measuring

P2744 「USACO5.3」量取牛奶 Milk Measuring

將桶按容積大小從小到大排序,令 \(f_{i,j}\) 表示前 \(i\) 個桶能否量出 \(j\) 夸脫,如果可以就用 vector 儲存最優方案。

先列舉桶的種類再列舉夸脫數,轉移看似只有兩種:之前有或沒有用過 \(i\) 號桶,新用一次 \(i\) 號桶。但這樣是錯誤的,可能存在中間狀態最優解長度較長的情況。具體例子可以看程式碼末尾。

所以我們還需要額外列舉用桶的次數而不是一次一次用,這樣最優情況就不會被覆蓋了。時間複雜度 \(O(Q^2\log P)\)

考慮將桶按容積從大到小排序,令 \(f_{i,j}\) 表示前 \(i\) 個桶能否量出 \(j\) 夸脫,如果可以就令 \(g_{i,j}\)

表示最優方案需要桶的數量,\(used_{i,j}\) 表示最優方案有沒有用 \(i\) 號桶。

這樣我們就可以一次一次用了,最優解肯定不會被覆蓋。

最後再根據記錄的資訊搜尋一遍,\(\require{cancel}\require{enclose}\sout{O(kQ^2P\log P)}\) 跑得飛快,其中 \(k\) 取決於方案長度,很小很小。

#include<bits/stdc++.h>
using namespace std;
#define N 105
#define NN 20005
#define For(i,x,y)for(i=x;i<=(y);i++)
#define Down(i,x,y)for(i=x;i>=(y);i--)
int g[N][NN],c[N];
/*vector<int>h[N][NN];*/
bool used[N][NN],f[N][NN];
vector<int>print(int now,int pos)
{
	if(g[pos][now]==1)return{c[pos]};
	/*if(!h[pos][now].empty())return h[pos][now];*/
	int i,j,k;
	vector<int>vec,tmp;
	Down(i,pos-1,1)
	{
		j=now;
		while(j>=c[pos])
		{
			j-=c[pos];
			/*cerr<<i<<' '<<j<<' '<<pos<<' '<<now<<' '<<g[i][j]<<' '<<g[pos][now]<<endl;*/
			if(used[i][j]&&g[i][j]+1==g[pos][now])
			{
				tmp=print(j,i);
				if(vec.empty())vec=tmp;
				else
				{
					Down(k,vec.size()-1,0)
					if(vec[k]!=tmp[k])break;
					if(~k&&vec[k]>tmp[k])vec=tmp;
				}
			}
		}
		if(!vec.empty())
		{
			vec.push_back(c[pos]);
			/*h[pos][now]=vec;*/
			return vec;
		}
	}
}
int main()
{
	int Q,p,i,j;
	vector<int>vec;
	cin>>Q>>p;
	For(i,1,p)cin>>c[i];
	sort(c+1,c+p+1,greater<int>());
	f[0][0]=1;
	For(i,1,p)
	For(j,0,Q)
	if(j<c[i])
	{
		f[i][j]=f[i-1][j];
		g[i][j]=g[i-1][j];
		used[i][j]=0;
	}
	else
	{
		if(!f[i-1][j]&&!f[i][j-c[i]])continue;
		if(!f[i-1][j]||f[i-1][j]&&f[i][j-c[i]]&&g[i][j-c[i]]+1-used[i][j-c[i]]<=g[i-1][j])f[i][j]=used[i][j]=1,g[i][j]=g[i][j-c[i]]+1-used[i][j-c[i]];
		else
		{
			f[i][j]=1;
			used[i][j]=0;
			g[i][j]=g[i-1][j];
		}
	}
	cout<<g[p][Q]<<' ';
	Down(i,p,1)
	if(used[i][Q])break;
	vec=print(Q,i);
	Down(i,vec.size()-1,0)cout<<vec[i]<<' ';
	return 0;
}
//#include<bits/stdc++.h>
//using namespace std;
//#define N 105
//#define NN 20005
//#define For(i,x,y)for(i=x;i<=(y);i++)
//int c[N];
//bool f[N][NN];
//vector<int>vec[N][NN];
//bool pd(vector<int>&x,vector<int>&y)
//{
//	return x.size()<y.size()||x.size()==y.size()&&x<y;
//}
//int main()
//{
//	bool bo;
//	int Q,p,i,j,k;
//	cin>>Q>>p;
//	For(i,1,p)cin>>c[i];
//	sort(c+1,c+p+1);
//	f[0][0]=1;
//	For(i,1,p)
//	For(j,0,Q)
//	{
//		f[i][j]=f[i-1][j];
//		vec[i][j]=vec[i-1][j];
//		For(k,1,j/c[i])
//		if(f[i-1][j-c[i]*k])
//		{
//			vec[i-1][j-c[i]*k].push_back(c[i]);
//			if(!f[i][j]||pd(vec[i-1][j-c[i]*k],vec[i][j]))f[i][j]=1,vec[i][j]=vec[i-1][j-c[i]*k];
//			vec[i-1][j-c[i]*k].pop_back();
//		}
//		/*if(j>=c[i]&&f[i-1][j-c[i]])
//		{
//			vec[i-1][j-c[i]].push_back(c[i]);
//			if(!f[i][j]||pd(vec[i-1][j-c[i]],vec[i][j]))f[i][j]=1,vec[i][j]=vec[i-1][j-c[i]];
//			vec[i-1][j-c[i]].pop_back();
//		}
//		if(j>=c[i]&&f[i][j-c[i]])
//		{
//			bo=0;
//			if(vec[i][j-c[i]].empty()||vec[i][j-c[i]].back()<c[i])bo=1,vec[i][j-c[i]].push_back(c[i]);
//			if(!f[i][j]||pd(vec[i][j-c[i]],vec[i][j]))f[i][j]=1,vec[i][j]=vec[i][j-c[i]];
//			if(bo)vec[i][j-c[i]].pop_back();
//		}
//		if(i==1&&j==3)cerr<<vec[i][j].size()<<endl;*/
//	}
//	/*p=3;
//	Q=7247+949*1;
//	cerr<<f[p][Q]<<' ';*/
//	cout<<vec[p][Q].size();
//	For(i,0,int(vec[p][Q].size())-1)cout<<' '<<vec[p][Q][i];
//	return 0;
//}
/*
16737
4
904
909
916
949
*/