题解 P2744 【[USACO5.3]量取牛奶Milk Measuring】
S1 一开始看到这道题还是很好确定算法的,妥妥的是dfs+dp。先跑一遍完全背包,vis[i][j]表示体积为j的时候选不选i最优,可以把第一维压掉。如果vis[j-a[i]]选了i,那么vis[j]就不用再选一遍。这样可以确定出来一共选了多少种木桶,之后搜索,再跑布尔完全背包,因为已经把桶的大小排过序了,那么如果可以拼成体积为q的就直接输出结束程序就可以了。
S2 注意两个小错误。
第一个,在最初的那一遍完全背包中,如果f[j-a[i]]+value等于f[j],那也要把vis[j]设为1,因为这样会最优——在同样的f[j]下,如果vis[j]=1,那么f[j+a[i]]的值会少加1,从而保证最终的结果最优。
第二个,这个真是无语,小错误害死人啊。在搜索完的那次完全背包中,我的初始化不是g[i]=big,而是g[q]=big……手一滑,三个人检查了一个小时,死活检查不出来,从网上下了数据才出来的……检查的时候一定要一个字一个字仔细看,不要因为语句简单就不看了,简单的语句也可能手滑打错。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define rint register int
#define inv inline void
#define big 1e9
using namespace std;
int p,q,a[101],f[20001],que[101],cnt;
bool use[101],vis[20001],g[20001];
inv dp()
{
for (rint i=0;i<=q;i++) g[i]=0;g[0]=1;
for (rint i=1;i<=f[q];i++)
for (rint j=a[que[i]];j<=q;j++)
if (g[j-a[que[i]]]) g[j]=1;
if (g[q])
{
printf("%d ",f[q]);
for (rint i=1;i<=f[q];i++) printf("%d ",a[que[i]]);
exit(0);
}
}
inv dfs(int x,int dep)
{
if (dep==f[q])
{
dp();
return;
}
if (f[q]-dep>p-x) return;
for (rint i=x+1;i<=p;i++)
{
que[dep+1]=i;
dfs(i,dep+1);
}
}
int main()
{
scanf("%d%d",&q,&p);
for (rint i=1;i<=p;i++) scanf("%d",&a[i]);
sort(a+1,a+p+1);
for (rint i=0;i<=q;i++) f[i]=big;f[0]=0;
for (rint i=1;i<=p;i++)
for (rint j=0;j<=q;j++)
{
vis[j]=0;
if (j>=a[i])
{
int value=vis[j-a[i]]^1;
if (f[j-a[i]]+value<=f[j])
{
f[j]=f[j-a[i]]+value;
vis[j]=1;
}
}
}
dfs(0,0);
}
总结:
-
要思考一下第一个搜出来的是不是最优解,是的话可以直接剪掉。
-
一定要注意细节,等于号加不加是个问题,仔细思考。出错的时候一句一句认真检查,千万不要嫌麻烦,发愁的时间比检查的时间长多了。