题解 UVA103 Stacking Boxes

【题意简述】

给你\(k\)\(n\)维盒子,求最多能把其中的几个盒子一层层装入

【题目分析】

这题数据范围较小,很多人可能第一眼看到会想到\(dfs\)或状态压缩\(dp\),实际上,这题并不需要指数级算法。

第一步显然是先预处理嵌套关系,这里有人有可能会用\(dfs\)一一搜索,但用一种贪心的思想可以更优。先来看一道题:

UVA11292

题意:有n个物品和m个箱子,分别有体积a和容积b,每个箱子有重量x,要求把n个物品分别放入m个箱子(每个箱子只能装一个物品),使得箱子的总重量最小。

这一题是《算法竞赛入门经典——训练指南》的例题一。对于一个容量大的箱子,显然要让它装一个体积大的物品,才不会浪费,所以可以按照箱子的容积从小到大排序,按物品的体积从小到大排序,然后一一对应。

回过头来,这题的箱子嵌套关系其实就是上题的特殊情况。那么我们也可以将每个箱子的每一维从小到大排序,这样就能预处理出每个箱子之间的嵌套关系了。

处理出大小关系后怎么办呢。题目要求输出路径,显然要用一种可以记录路径的做法处理,这里我采用了拓扑排序,因为它思路简单,效率高。

具体做法如下:

  1. 输入,并将每个箱子的每一维从小到大排序。
  2. 枚举箱子,处理出两两之间的嵌套关系。从小箱子向大箱子连一条有向边,并将大箱子的入度\(++\)
  3. 拓扑排序计算\(dp\),并存下路径。
  4. 统计答案。
  5. \(dfs\)寻找路径,输出。

实现细节见代码:

#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
int read(){
	int w=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
	while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
	return w*h;
}
int k,n;
int box[35][15];
struct node{
	int next,to;
}edge[200010];
int head[35],num;
int in[35],dp[35],from[35];//dp[i]表示以i为最外层的箱子最多能嵌套的个数,from[i]记录第i个箱子嵌套最多时i里面装的是哪个箱子
queue<int>q;
void add(int u,int v){
	edge[++num].next=head[u];
	edge[num].to=v;
	head[u]=num;
}
void topo(){
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=k;i++)
		if(!in[i])q.push(i),dp[i]=1,from[i]=-1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=edge[i].next){
			int v=edge[i].to;
			in[v]--;
//			dp[v]=max(dp[v],dp[u]+1);
			if(dp[u]+1>dp[v]){
				dp[v]=dp[u]+1;
				from[v]=u;
			}
			if(!in[v])q.push(v);
		}
	}
}
void dfs(int u){
	if(from[u]==-1){
		printf("%lld ",u);
		return;
	}
	dfs(from[u]);
	printf("%lld ",u);//注意先迭代后输出,迭代到底后要输出再返回
}
signed main(){
	while(scanf("%lld%lld",&k,&n)!=EOF){
//		k=read();n=read();
		memset(in,0,sizeof(in));
		memset(box,0,sizeof(box));
		memset(from,0,sizeof(from));
		memset(head,0,sizeof(head));//多次输入需初始化
		num=0;
		for(int i=1;i<=k;i++){
			for(int j=1;j<=n;j++)
				box[i][j]=read();
			sort(box[i]+1,box[i]+n+1);//对每个箱子每一维从小到大排序
		}
		for(int i=1;i<=k;i++)
			for(int j=1;j<=k;j++){
				bool flag=1;
				for(int l=1;l<=n;l++)
					if(box[i][l]<=box[j][l])flag=0;
				if(flag){
					add(j,i);//建边
					in[i]++;
//					printf("%lld can put into %lld\n",j,i);
				}
			}
		topo();//拓扑排序
		int ans=0,id;
		for(int i=1;i<=k;i++){
//			ans=max(ans,dp[i]);
			if(dp[i]>ans){
				ans=dp[i];//统计答案
				id=i;
			}
		}
		printf("%lld\n",ans);
		dfs(id);puts("");
	}
	return 0;
}
posted @ 2021-11-04 13:54  pidan007  阅读(53)  评论(0编辑  收藏  举报