AYIT609暑假集训第一周下训练题题解 - 记忆化搜索和背包
Changing Digits
来源:POJ - 3373
题意:
现在给你两个数n , k. 要求一个新的数m
满足四个要求:
1、m没有前导0和长度和n一样长.
2、可以被k整除.
3、满足1,2的前提下, 尽量满足m和n的每位尽量相同.
4、满足1,2,3的前提下, 使m最小.
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stacp>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define PI acos(-1)
const int N=129;
int p,L,Mod,rem[N][N*N],ans[N],num[N],mod[N][12];
char s[N];
bool dfs(int L,int pos,int M)
{
if(M==0)//余数为0得到解
{
for(int i=L-1;i>=0;--i)
printf("%d",ans[i]);
printf("\n");
return 1;
}
if(L==0||rem[L][M]>pos)
return 0;//关键剪枝
for(int i=pos;i>=0;i--) //搜索比N小的从高位开始
{
for(int j=0;j<num[i];j++)
{
if(i==L-1&&j==0)
continue;
ans[i]=j;
int tmp=M-(mod[i][num[i]]-mod[i][j]);
tmp%=p;
if(tmp<0)
tmp+=p;
if(dfs(L-1,i-1,tmp))
return 1;
}
ans[i]=num[i];
}
for(int i=0;i<=pos;i++)//搜索比n大的从低位开始
{
for(int j=num[i]+1;j<10;j++)
{
ans[i]=j;
int tmp=M+mod[i][j]-mod[i][num[i]];
tmp%=p;
if(tmp<0)
tmp+=p;
if(dfs(L-1,i-1,tmp))
return 1;
}
ans[i]=num[i];
}
rem[L][M]=pos+1;
return 0;
}
int main()
{
while(~scanf("%s",s))
{
scanf("%d",&p);
L=strlen(s);
mem(rem,0);
for(int i=0;i<10;++i)
mod[0][i]=i%p;
for(int i=1;i<L;++i)
{
for(int j=0;j<10;++j)
mod[i][j]=(mod[i-1][j]*10)%p;
}
Mod=0;//计算s模p的余数,将s倒置
for(int i=0;i<L;++i)
{
ans[i]=num[i]=s[L-i-1]-'0';
Mod+=mod[i][num[i]];
Mod%=p;
}
for(int i=0;i<=L;++i)
{
if(dfs(i,L-1,Mod))
break;
}
}
return 0;
}
How many ways
来源:HDU - 1978
记忆化搜索AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[110][110];
int b[110][110];
int n,m;
int dfs(int x,int y)
{
if(b[x][y]>=0)
return b[x][y];
b[x][y]=0;
for(int i=0; i<=a[x][y]; i++)
{
for(int j=0; j<=a[x][y]-i; j++)
{
int tx=x+i;
int ty=y+j;
if(tx<0||ty<0||tx>=n||ty>=m)
continue;
b[x][y]=(b[x][y]+dfs(tx,ty))%10000;
}
}
return b[x][y];
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
memset(b,-1,sizeof(b));
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
scanf("%d",&a[i][j]);
b[n-1][m-1]=1;
printf("%d\n",dfs(0,0));
}
return 0;
}
dp AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string.h>
#include<queue>
#include<iostream>
#include<stack>
using namespace std;
typedef long long ll;
const int mod=1e4;
int a[110][110],dp[110][110];
int main()
{
int t,n,m;
cin>>t;
while(t--)
{
memset(dp,0,sizeof(dp));
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
}
dp[n][m]=1;
for(int i=n;i>=1;i--)
{
for(int j=m;j>=1;j--)
{
for(int p=0;p<=a[i][j]&&(i+p<=n);p++)
{
int ww=a[i][j]-p;
for(int q=0;q<=ww&&(j+q<=m);q++)
{
if(p==0&&q==0)
continue;
dp[i][j]=(dp[i][j]+dp[i+p][j+q])%mod;
}
}
}
}
int w=dp[1][1];
printf("%d\n",w);
}
return 0;
}
B-number
来源:HDU - 3652
题意:
求1~N中包含13且能被13整除的数的数量。
思路:
dp[i][j][k],i表示位数,j表示余数,k表示末尾是1、末尾不是1、含有13.。
数位dp模板题。
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stacp>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define PI acos(-1)
const int N=15;
int a[N],dp[N][N][3];
int dfs(int pos,int pre,int mod,int limit)
{
if(pos==-1)
return (pre==2&&!mod);
if(!limit&&dp[pos][mod][pre]!=-1)
return dp[pos][mod][pre];
int up=limit?a[pos]:9;
int tmp=0;
for(int i=0;i<=up;i++)
{
int tmod=(mod*10+i)%13;
if((pre==1&&i==3)||pre==2)
tmp+=dfs(pos-1,2,tmod,limit&&a[pos]==i);
else
tmp+=dfs(pos-1,i==1,tmod,limit&&a[pos]==i);
}
if(!limit)
dp[pos][mod][pre]=tmp;
return tmp;
}
int solve(int x)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x/=10;
}
return dfs(pos-1,0,0,1);
}
int main()
{
int n;
memset(dp,-1,sizeof(dp));
while(cin>>n)
printf("%d\n",solve(n));
return 0;
}
Cash Machine
来源:POJ - 1276
题意:给出几组不同数量不同面值的钱币,给你一个cash,要你求出不超过cash的金额。
思路:多重背包模板题。
AC代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
int w[100020];
int dp[100020];
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int cash;
int a,b;
while(cin>>cash)
{
memset(w,0,sizeof(w));
memset(dp,0,sizeof(dp));
int n;
cin>>n;
int p=0;
for(int i=0;i<n;i++)
{
cin>>a>>b;
int x=1;
while(x<a)
{
w[p++]=x*b;
a=a-x;
x*=2;
}
if(a)
w[p++]=a*b;
}
for(int i=0;i<p;i++)
{
for(int j=cash;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
}
cout<<dp[cash]<<endl;
}
return 0;
}
饭卡
来源:HDU - 2546
思路:01背包模板题
AC代码:
#include<bits/stdc++.h>
using namespace std;
int a[1010],dp[1010];
int main()
{
int n,m;
while(~scanf("%d",&n)&&n)
{
memset(dp,0,sizeof(dp));
for(int i=0; i<n; i++)
scanf("%d",&a[i]);
scanf("%d",&m);
if(m>=5)
{
sort(a,a+n);
for(int i=0; i<n-1; i++)
{
for(int j=m-5; j>=a[i]; j--)
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
printf("%d\n",m-a[n-1]-dp[m-5]);
}
else
printf("%d\n",m);
}
return 0;
}
Robberies
来源:HDU - 2955
题意:
小偷去100家银行,偷每家都有被抓概率被抓,概率p[i],偷不同银行的事件之间相互独立。
现在小偷希望偷到更多的钱,但是被抓的概率不能超过P,问最多能偷多少钱。
思路:
01背包一点点变形。
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stacp>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define PI acos(-1)
double dp[500010],w[1010];
int v[1010];
int main()
{
int n;
sc(n);
while(n--)
{
mem(dp,0);
dp[0]=1;
int a,sum=0;
double p;
scc(p,a);
p=1-p;
for(int i=1;i<=a;i++)
{
scc(v[i],w[i]);
w[i]=1-w[i];
sum+=v[i];
}
for(int i=1;i<=a;i++)
{
for(int j=sum;j>=v[i];j--)
dp[j]=max(dp[j-v[i]]*w[i],dp[j]);
}
for(int i=sum;i>=0;i--)
{
if(dp[i]>p)
{
cout<<i<<endl;
break;
}
}
}
return 0;
}
Dividing
来源:POJ - 1014
题意:
有价值为1~6的6个宝石,给出每种价值的宝石数量,问是是否能选出部分,使选出部分的价值是总价值的一半。
思路:
多重背包模板题,可以加个二进制优化一下。
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stacp>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define PI acos(-1)
const int N=1e6+20;
int a[10],b[N];
bool dp[N];
int main()
{
int cas=0;
while(~scanf("%d %d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6]))
{
if(a[1]+a[2]+a[3]+a[4]+a[5]+a[6]==0)
break;
int sum=0;
for(int i=1;i<=6;i++)
sum+=a[i]*i;
printf("Collection #%d:\n",++cas);
if(sum&1)
{
printf("Can't be divided.\n\n");
continue;
}
int cnt=0;
for(int i=1;i<=6;i++)
{
int j=0;
while(a[i]>=(1<<j))
{
b[++cnt]=(1<<j)*i;
a[i]-=(1<<j);
j++;
}
if(a[i]>0)
b[++cnt]=a[i]*i;
}
int mid=sum/2;
for(int i=0;i<=mid;i++)
dp[i]=0;
dp[0]=1;
for(int i=1;i<=cnt;i++)
{
for(int j=mid;j>=b[i];j--)
{
if(dp[j-b[i]])
dp[j]=1;
}
}
if(dp[mid])
printf("Can be divided.\n");
else
printf("Can't be divided.\n");
printf("\n");
}
return 0;
}
悼念512...
悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
来源:HDU - 2191
思路:多重背包模板题。
AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string.h>
#include<queue>
#include<iostream>
#include<stack>
using namespace std;
typedef long long ll;
int dp[110],pr[110],w[110],d[110];
int main()
{
int t,sum,m;
cin>>t;
while(t--)
{
memset(dp,0,sizeof(dp));
cin>>sum>>m;
for(int i=0; i<m; i++)
cin>>pr[i]>>w[i]>>d[i];
for(int i=0; i<m; i++) //种类
{
for(int j=0; j<d[i]; j++)//袋数
{
for(int k=sum; k>=pr[i]; k--)
{
dp[k]=max(dp[k],dp[k-pr[i]]+w[i]);
}
}
}
printf("%d\n",dp[sum]);
// for(int i=0;i<m;i++)
// {
// for(int j=0;j<w[i];j++)
// {
// for(int k=0;k<=d[i];k++)
// {
// if(k*w[i]<=sum)
// dp[i][j]=max(dp[i-1][j-k*w[i]]+k*v[i],dp[i-1][j]);
// else
// dp[i][j]=dp[i-1][j];
// }
//
// }
// }
// printf("%d\n",dp[m][])
}
return 0;
}