好久前做的题了,今天总结写这篇博文的时候UVA正好挂了……
题意:要把cd上的音乐导到磁带里,要求尽量使磁带剩余的空间小并按输入顺序顺序打印出每次磁带中的每个音轨长度。
思路:因为不久前学过状态压缩,所以马上想到用01记录状态。总数不超过20个,用int数记录状态,对每一位:0表示不取,1表示取。
#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))
int data[25],v,n;
int dp[10005];
int pre[10005];
void Output (int str)
{
for (int i=1;i<=n;i++)
if ((str>>i)&1)
printf("%d ",data[i]);
}
int main ()
{
while (~scanf("%d %d",&v,&n))
{
int i;
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
for (i=1;i<=n;i++)
scanf("%d",&data[i]);
for (i=1;i<=n;i++)
for (int j=v;j>=data[i];j--) /
if (dp[j]<dp[j-data[i]]+data[i])
{
dp[j]=dp[j-data[i]]+data[i];
pre[j]=pre[j-data[i]]|(1<<i);
}
Output (pre[v]);
printf("sum:%d\n",dp[v]);
}
return 0;
}
注释部分之后的内容是优化过的,思路和导出01背包一维解法的方法一样。下面给出过程。
最开始我的写法是这样的(pre是二维数组):
for (int i=1;i<=n;i++)
{
for (int j=v;j>=data[i];j--)
{
if (dp[j]>dp[j-data[i]]+data[i])
{
dp[j]=dp[j];
pre[i][j]=pre[i-1][j];
}
else
{
dp[j]=dp[j-data[i]]+data[i];
pre[i][j]=pre[i-1][j-data[i]]|(1<<i);
}
}
for (int j=0;j<data[i];j++) //需要将上一行的状态传递给下一行
pre[i][j]=pre[i-1][j];
}
然后优化到一维:
for (i=1;i<=n;i++)
for (int j=v;j>=data[i];j--)
if (dp[j]>dp[j-data[i]]+data[i])
{
dp[j]=dp[j];
pre[j]=pre[j];
}
else
{
dp[j]=dp[j-data[i]]+data[i];
pre[j]=pre[j-data[i]]|(1<<i);
}
下面贴一份比赛中共享的一份代码,用另一种思路做的:
#include <stdio.h>
#define max 1000005
int f[max][25]; //f[i][j]到达i状态时j是否使用
int main ()
{
int n;
int t;
int a[25];
int i,j,l;
int ans;
while(scanf("%d",&n)!=EOF)
{
ans=0;
scanf("%d",&t);
for(i=1;i<=t;i++)
scanf("%d",&a[i]);
for(i=0;i<=n;i++)
for(j=0;j<=t;j++)
f[i][j]=0;
f[0][0]=1;
for (i=1;i<=t;i++) //1~t物品标号
for (j=n-a[i];j>=0;j--)
{
if (f[j][0]==0)
continue;
f[j+a[i]][0]=1;
for (l=1;l<=t;l++)
f[j+a[i]][l]=f[j][l];
f[j+a[i]][i]=1;
if (j+a[i]>ans)
ans=j+a[i];
}
for (i=1;i<=t;i++)
if (f[ans][i])
printf("%d ",a[i]);
printf("sum:%d\n",ans);
}
return 0;
}
在贴一份网上的解法,和我最开始的思路一样,不过当时我没有实现出来……
http://www.cppblog.com/syhd142/articles/118968.html