【题意简述】
给你\(k\)个\(n\)维盒子,求最多能把其中的几个盒子一层层装入
【题目分析】
这题数据范围较小,很多人可能第一眼看到会想到\(dfs\)或状态压缩\(dp\),实际上,这题并不需要指数级算法。
第一步显然是先预处理嵌套关系,这里有人有可能会用\(dfs\)一一搜索,但用一种贪心的思想可以更优。先来看一道题:
题意:有n个物品和m个箱子,分别有体积a和容积b,每个箱子有重量x,要求把n个物品分别放入m个箱子(每个箱子只能装一个物品),使得箱子的总重量最小。
这一题是《算法竞赛入门经典——训练指南》的例题一。对于一个容量大的箱子,显然要让它装一个体积大的物品,才不会浪费,所以可以按照箱子的容积从小到大排序,按物品的体积从小到大排序,然后一一对应。
回过头来,这题的箱子嵌套关系其实就是上题的特殊情况。那么我们也可以将每个箱子的每一维从小到大排序,这样就能预处理出每个箱子之间的嵌套关系了。
处理出大小关系后怎么办呢。题目要求输出路径,显然要用一种可以记录路径的做法处理,这里我采用了拓扑排序,因为它思路简单,效率高。
具体做法如下:
- 输入,并将每个箱子的每一维从小到大排序。
- 枚举箱子,处理出两两之间的嵌套关系。从小箱子向大箱子连一条有向边,并将大箱子的入度\(++\)。
- 拓扑排序计算\(dp\),并存下路径。
- 统计答案。
- 用\(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;
}