sduacm16级寒假训练 动态规划(一)

https://vjudge.net/contest/148555
Password: acmlab2016
Hint: 均为模板题

A - 最长递增子序列
【题意】
求最长上升子序列(LIS)的长度。
【思路】
由于n<=1000,直接O(n^2)就可以
设f[i]表示以a[i]为结尾的最长上升子序列的长度

状态转移方程为f[i]=max{f[i],f[j]+1} (j

#include<cstdio>
#include<algorithm>
using namespace std;
const int Mx=1010;
int f[Mx];
int n,a[Mx];
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        f[i]=1;
    }
    int ans=0;
    for (int i=1;i<=n;i++)
        for (int j=0;j<i;j++)
            if (a[j]<a[i])
            {
                f[i]=max(f[i],f[j]+1);
                ans=max(ans,f[i]);
            }
    printf("%d\n",ans);
    return 0;
}

B - 最长公共子序列
【题意】
求最长公共子序列的长度。
【思路】
设两个字符串分别为s1和s2
f[i][j]表示s1的前i个字符和s2的前j个字符的最长公共子序列
如果s1[i] == s2[j]
f[i][j]=f[i-1][j-1]+1;
如果s1[i] != s2[j]
f[i][j]=max(f[i-1][j], f[i][j-1]);
【Code】

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Mx=1010;
int F[Mx][Mx];
char s1[Mx],s2[Mx];

int main()
{
    while (~scanf("%s%s", s1+1, s2+1))
    {
        int n=strlen(s1+1),m=strlen(s2+1);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (s1[i]==s2[j]) F[i][j]=F[i-1][j-1]+1;
                else F[i][j]=max(F[i-1][j],F[i][j-1]);
        printf("%d\n", F[n][m]);
    }
    return 0;
}

C - 01背包
【题意】
体积为V的背包最多能装价值为多少的物品
【思路】
裸的P01.
【Code】

#include<cstdio>
#include<algorithm>
int f[1010],v[1010],w[1010];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int m,n;
        scanf("%d %d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%d",&v[i]);
        for (int i=1;i<=n;i++) scanf("%d",&w[i]);
        for (int i=0;i<=m;i++) f[i]=0;
        for (int i=1;i<=n;i++)
            for (int j=m;j>=w[i];j--)
                f[j]=std::max(f[j],f[j-w[i]]+v[i]);
        printf("%d\n",f[m]);
    }
}

D - 完全背包
【题意】
给定一个储钱罐的容量M,给定N种硬币的价值和重量,问是否能填满储钱罐,能填满则输出储钱罐内钱的最小值
【思路】
P02.
【Code】

#include<cstdio>
#include<algorithm>
int w[505],v[505],f[10050];
const int INF=0x3FFFFFFF;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m,e,t;
        scanf("%d %d",&e,&t);
        m=t-e;
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d %d",&v[i],&w[i]);
        for (int i=0;i<=m;i++) f[i]=INF;
        f[0]=0;
        for (int i=1;i<=n;i++)
            for(int j=w[i];j<=m;j++)
            {
                f[j]=std::min(f[j],f[j-w[i]]+v[i]);
                //printf("f[%d,%d]=%d\n",i,j,f[j]);
            }
        if (f[m]!=INF) printf("The minimum amount of money in the piggy-bank is %d.\n",f[m]);
        else printf("This is impossible.\n");
    }
}

E - 多重背包
【题意】
给定6种价值的若干件物品,问是否能按价值平分物品.
【思路】
如果总价值为奇数,一定无解.
然后用的方法为判断性背包,不详细解释了.
【Code】

#include<cstdio>
#include<cstring>
int c[7];
bool f[200000];
int main()
{
    int T=0;
    while (++T)
    {
        int m=0;
        for (int i=1;i<=6;i++)
        {
            scanf("%d",&c[i]);
            m+=i*c[i];//一定注意这里是i*c[i]不是c[i]
        }
        if (m==0) break;
        printf("Collection #%d:\n",T);
        if (m%2==1)
        {
            printf("Can't be divided.\n\n");
            continue;
        }
        m/=2;
        memset(f,false,sizeof(f));
        /*一开始用for(int i=1;i<=m;i++) f[i]=false;
          不知道为什么会Wa..
          有知道的可以告诉我一声*/
        f[0]=true;
        int cnt;
        for (int i=1;i<=6;i++)
        {
            if (c[i]==0) continue;
            for (int j=1;j<=c[i];j*=2)
            {
                cnt=i*j;
                for (int k=m;k>=cnt;k--)
                {
                    if (f[k-cnt]) f[k]=true;
                    //printf("f[%d]= %d.\n",k-cnt,f[k]?1:0);
                }
                c[i]-=j;
            }
            cnt=c[i]*i;
            if (cnt)
            {
                for (int k=m;k>=cnt;k--)
                {
                    if (f[k-cnt]) f[k]=true;
                    //printf("f[%d]= %d\n",k-cnt,f[k]?1:0);
                }
            }
        }
        if (f[m]) printf("Can be divided.\n\n");
        else printf("Can't be divided.\n\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值