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

扫描二维码关注公众号,回复: 11439703 查看本文章

题意:给出几组不同数量不同面值的钱币,给你一个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;
}

猜你喜欢

转载自www.cnblogs.com/OFSHK/p/13377429.html