LightOJ 1231 Coin Change (I)
题目链接 http://lightoj.com/volume_showproblem.php?problem=1231
题意:给你n个物品的体积和数量,让你求有多少种组合能恰好装满M体积的背包。
思路:最开始想的是多重背包和母函数,发现都不太合适。最后还是回归最基本的dp吧。
dp[i][j]表示前i种物品,组成j的容量有几种取法,枚举第i种物品取得数量,注意可以不取。
#include <cstdio>
#include <cstring>
int n,sum,v;
int dp[55][1005],c[55],num[55];
const int mod=100000007;
int main ()
{
int T;
scanf("%d",&T);
for (int Cas=1;Cas<=T;Cas++)
{
int i;
scanf("%d%d",&n,&v);
sum=0;
memset(dp,0,sizeof(dp));
for (i=1;i<=n;i++)
scanf("%d",&c[i]);
for (i=1;i<=n;i++)
scanf("%d",&num[i]);
for (int ss=1;ss<=n;ss++)
for (i=1;i<=num[ss];i++)
if (i*c[ss]<=v)
dp[ss][i*c[ss]]=1;
for (i=2;i<=n;i++)
for (int j=1;j<=v;j++)
for (int k=0;k<=num[i];k++)
if (j-k*c[i]>0)
dp[i][j]=(dp[i][j]%mod+dp[i-1][j-k*c[i]]%mod)%mod;
printf("Case %d: %d\n",Cas,dp[n][v]%mod);
}
return 0;
}
优化后的代码,效率提高很多:
#include <cstdio>
#include <cstring>
int n,v;
int dp[55][1005],c[55],num[55];
const int mod=100000007;
int main ()
{
int T,i;
scanf("%d",&T);
for (int Cas=1;Cas<=T;Cas++)
{
scanf("%d%d",&n,&v);
memset(dp,0,sizeof(dp));
for (i=1;i<=n;i++)
scanf("%d",&c[i]);
for (i=1;i<=n;i++)
scanf("%d",&num[i]);
dp[0][0]=1;
for (i=1;i<=n;i++)
for (int j=0;j<=v;j++)
for (int k=0;j+k*c[i]<=v && k<=num[i];k++)
{
dp[i][j+k*c[i]]+=dp[i-1][j];
dp[i][j+k*c[i]]%=mod;
}
printf("Case %d: %d\n",Cas,dp[n][v]%mod);
}
return 0;
}
题目链接:http://lightoj.com/volume_showproblem.php?problem=1232
题意:和上面那道差不多,只是每个最多只能用k次,看起来很麻烦,但其实就是最水的完全背包……
#include <cstdio>
#include <cstring>
#define max(x,y) ((x)>(y)?(x):(y))
const int mod=100000007;
int w[105],dp[100005];
int n,v;
void CompletePack ()
{
int i,j;
memset(dp,0,sizeof(dp));
dp[0]=1;
for (i=1;i<=n;i++)
for (j=w[i];j<=v;j++)
dp[j]=(dp[j]+dp[j-w[i]])%mod;
}
int main ()
{
int T;
scanf("%d",&T);
for (int Cas=1;Cas<=T;Cas++)
{
scanf("%d%d",&n,&v);
for (int i=1;i<=n;i++)
scanf("%d",&w[i]);
CompletePack ();
printf("Case %d: %d\n",Cas,dp[v]);
}
return 0;
}
LightOJ 1122 Digit Count
题意:给出数字集合S,求由S中元素构成长度为N且相邻位数字只差不超过2的数字有几个。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
int dp[12][12]; //dp[i][j] 表示长度为i 以数字data[j]结尾的方案数
int data[12];
int main ()
{
#ifdef ONLINE_JUDGE
#else
freopen("read.txt","r",stdin);
#endif
int T,n,m;
scanf("%d",&T);
for (int Cas=1;Cas<=T;Cas++)
{
printf("Case %d: ",Cas);
memset(dp,0,sizeof(dp));
scanf("%d%d",&m,&n);
int i;
for (i=1;i<=m;i++)
scanf("%d",&data[i]);
for (i=1;i<=m;i++)
dp[1][i]=1;
for (i=2;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=1;k<=m;k++)
if (abs(data[k]-data[j])<=2)
dp[i][j]+=dp[i-1][k];
int ans=0;
for (i=1;i<=m;i++)
ans+=dp[n][i];
printf("%d\n",ans);
}
return 0;
}
Lightoj 1047 Neighbor House
题意:有n户人,打算把他们的房子图上颜色,有red、green、blue三种颜色,每家人涂不同的颜色要花不同的费用,而且相邻两户人家之间的颜色要不同,求最小的总花费费用
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
#define min3(a,b,c) min((a),min((b),(c)))
int data[25][4];
int dp[25][4];//dp[i][j]第i户选j时的最小钱数
int main ()
{
#ifdef ONLINE_JUDGE
#else
freopen("read.txt","r",stdin);
#endif
int T;
scanf("%d",&T);
for (int Cas=1;Cas<=T;Cas++)
{
int i,n,a,b;
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d%d%d",&data[i][1],&data[i][2],&data[i][3]);
memset(dp,0,sizeof(dp));
for (i=1;i<=3;i++)
dp[1][i]=data[1][i];
for (i=2;i<=n;i++)
for (int j=1;j<=3;j++)
{
if (j==1) a=2,b=3;
if (j==2) a=1,b=3;
if (j==3) a=2,b=1;
dp[i][j]=data[i][j]+min(dp[i-1][a],dp[i-1][b]);
// printf("%d %d %d\n",j,a,b);
}
int ans=min3(dp[n][1],dp[n][2],dp[n][3]);
printf("Case %d: %d\n",Cas,ans);
}
return 0;
}