nyoj 入门级dp题目题解集

79-拦截导弹


内存限制:64MB  时间限制:3000ms  Special Judge: No

accepted:1  submit:2

题目描述:

某国为了防御敌国的导弹袭击,发展中一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于等于前一发的高度。某天,雷达捕捉到敌国导弹来袭。由于该系统还在试用阶段,所以只用一套系统,因此有可能不能拦截所有的导弹。

输入描述:

第一行输入测试数据组数N(1<=N<=10) 接下来一行输入这组测试数据共有多少个导弹m(1<=m<=20) 接下来行输入导弹依次飞来的高度,所有高度值均是大于0的正整数。

输出描述:

输出最多能拦截的导弹数目

样例输入:

复制
2
8
389 207 155 300 299 170 158 65
3
88 34 65

样例输出:

6
2
题解:求最长非增子序列,dp[i]表示前i导弹最多能阻拦多少个。dp[i]=max(dp[j]),  dp[j]>dp[i],j<i。
#include <iostream>
using namespace std;
int main()
{
    int t,n,a[20],dp[20];
    cin>>t;
    while(t--)
    {
        int maxx=-1;
        cin>>n;
        for (int i=0; i<n; i++)
            cin>>a[i];
        fill(dp,dp+20,1);
        for (int i=0; i<n; i++)
            for (int j=0; j<i; j++)
                if(a[j]>a[i])
                {
                    dp[i]=max(dp[i],dp[j]+1);
                    if(dp[i]>maxx)
                        maxx=dp[i];
                }
        cout<<maxx<<endl;
    }
    return 0;
}

17-单调递增最长子序列


内存限制:64MB  时间限制:3000ms  Special Judge: No

accepted:4  submit:8

题目描述:

求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4

输入描述:

第一行一个整数0<n<20,表示有n个字符串要处理 随后的n行,每行有一个字符串,该字符串的长度不会超过10000

输出描述:

输出字符串的最长递增子序列的长度

样例输入:

复制
3
aaa
ababc
abklmncdefg

样例输出:

1
3
7

题解:状态转移方程:dp[i]=max(dp[j]),  dp[j]<dp[i],j<i。

#include <iostream>
using namespace std;
int main()
{
    int t,dp[10001];
    string str;
    cin>>t;
    while(t--)
    {
        int maxx=1;
        cin>>str;
        fill(dp,dp+10001,1);
        for (int i=0; i<str.size(); i++)
            for (int j=0; j<i; j++)
                if(str[j]<str[i])
                {
                    dp[i]=max(dp[i],dp[j]+1);
                    maxx=max(dp[i],maxx);
                }
        cout<<maxx<<endl;
    }
    return 0;
}

37-回文字符串


内存限制:64MB  时间限制:3000ms  Special Judge: No

accepted:2  submit:3

题目描述:

所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba"。当然,我们给你的问题不会再简单到判断一个字符串是不是回文字符串。现在要求你,给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。

输入描述:

第一行给出整数N(0<N<100) 接下来的N行,每行一个字符串,每个字符串长度不超过1000.

输出描述:

每行输出所需添加的最少字符数

样例输入:

复制
1
Ab3bd

样例输出:

2

题解:求最长公共子序列。添加字符=原串长-最长公共子序列长。

dp[i][j]表示长为i的s1前缀和长为j的s2前缀的最长公共子序列长。

状态转移方程:s1[i]==s2[j]  dp[i][j]=dp[i-1][j-1]+1;

                      s1!=s2[j]  dp[i][j]=max(dp[i-1][j],dp[i][j-1]);


#include <iostream>
#include <algorithm>
using namespace std;
int dp[1001][1001];
int main()
{
    int t;
    string s1,s2;
    cin>>t;
    while(t--)
    {
        cin>>s1;
        int len=s1.size();
        s2=s1;
        reverse(s2.begin(),s2.end());
        fill(dp[0],dp[0]+(len+1)*(len+1),0);
        for (int i=0;i<len;i++)
            for (int j=0;j<len;j++)
            {
                if(s1[i]==s2[j])
                    dp[i+1][j+1]=dp[i][j]+1;
                else
                    dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
            }
        cout<<len-dp[len][len]<<endl;
    }
    return 0;
}



阅读量:238

104-最大和


内存限制:64MB  时间限制:1000ms  Special Judge: No

accepted:2  submit:3

题目描述:

给定一个由整数组成二维矩阵(r*c),现在需要找出它的一个子矩阵,使得这个子矩阵内的所有元素之和最大,并把这个子矩阵称为最大子矩阵。 
例子:
0 -2 -7 0 
9 2 -6 2 
-4 1 -4 1 
-1 8 0 -2 
其最大子矩阵为:

9 2 
-4 1 
-1 8 
其元素总和为15。 

输入描述:

第一行输入一个整数n(0<n<=100),表示有n组测试数据; 每组测试数据: 第一行有两个的整数r,c(0<r,c<=100),r、c分别代表矩阵的行和列; 随后有r行,每行有c个整数;

输出描述:

输出矩阵的最大子矩阵的元素之和。

样例输入:

复制
1
4 4
0 -2 -7 0 
9 2 -6 2 
-4 1 -4 1 
-1 8 0 -2 

样例输出:

15

题解:将二维压缩成一维,然后转化为求最大子串和。

#include <iostream>
using namespace std;
int main ()
{
    int t,n,m,a[101][101];
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        fill(a[0],a[0]+101*101,0);
        for (int i=1; i<=n; i++)
            for (int j=1; j<=m; j++)
            {
                cin>>a[i][j];
                a[i][j]+=a[i-1][j];//求第j列的前i项和。
            }
        int mx=a[1][1];
        for (int i=1; i<=n; i++)
            for (int j=i; j<=n; j++)
            {
                int sum=0;
                for (int k=1; k<=m; k++)
                {
                    int tmp=a[j][k]-a[i-1][k];
                    sum=(sum>=0?sum:0)+tmp;
                    mx=max(mx,sum);
                }
            }
        cout<<mx<<endl;
    }
    return 0;
}


325-zb的生日


内存限制:64MB  时间限制:3000ms  Special Judge: No

accepted:4  submit:11

题目描述:

今天是阴历七月初五,acm队员zb的生日。zb正在和C小加、never在武汉集训。他想给这两位兄弟买点什么庆祝生日,经过调查,zb发现C小加和never都很喜欢吃西瓜,而且一吃就是一堆的那种,zb立刻下定决心买了一堆西瓜。当他准备把西瓜送给C小加和never的时候,遇到了一个难题,never和C小加不在一块住,只能把西瓜分成两堆给他们,为了对每个人都公平,他想让两堆的重量之差最小。每个西瓜的重量已知,你能帮帮他么?

输入描述:

多组测试数据(<=1500)。数据以EOF结尾 第一行输入西瓜数量N (1 ≤ N ≤ 20) 第二行有N个数,W1, …, Wn (1 ≤ Wi ≤ 10000)分别代表每个西瓜的重量

输出描述:

输出分成两堆后的质量差

样例输入:

复制
5
5 8 13 27 14

样例输出:

3

题解:若想两部分尽量相等,那么只要考虑一个部分,让它接近它们总和的一半。

dp[j]表示重量为j能否凑成,当然j最大为sum/2。

状态转移方程:if(dp[j-w[i]]==1)dp[j]=dp[j-w[i]]。

#include <iostream>
int dp[100001];
using namespace std;
int main ()
{
    int n,w[21],m,sum;
    while(cin>>n)
    {
        m=sum=0;
        fill(dp,dp+100001,0);
        for (int i=0; i<n; i++)
        {
            cin>>w[i];
            sum+=w[i];
        }
        dp[0]=1;
        for (int i=0; i<n; i++)
        {
            (m+w[i]<sum/2)?m+=w[i]:m=sum/2;
            for (int j=m; j>=w[i]; j--)
                if(dp[j-w[i]])
                    dp[j]=1;
        }
        int i;
        for (i=sum/2;!dp[i];i--);//找出最接近sum/2的重量。
        cout<<sum-2*i<<endl;
    }
    return 0;
}

168-房间安排


内存限制:64MB  时间限制:3000ms  Special Judge: No

accepted:8  submit:19

题目描述:

2010年上海世界博览会(Expo2010),是第41届世界博览会。于2010年5月1日至10月31日期间,在中国上海市举行。本次世博会也是由中国举办的首届世界博览会。上海世博会以“城市,让生活更美好”(Better City,Better Life)为主题,将充分探索21世纪城市生活。

这次世博会总投资达450亿人民币,创造了世界博览会史上的最大规模记录。吸引200个国家和国际组织参展。预计有7000万人次的参观者。

为了更好地接待在这期间来自世界各地的参观者,如何合理安排各宾馆的住房问题提到了日程。组委会已接到了大量的客户住宿定单,每张定单的内容包括要住宿的房间数,开始住宿时间和要住的天数。为了便于整个城市各宾馆的管理,组委会希望对这些定单进行安排,目的是用尽可能少的房间来满足这些定单,以便空出更多的房间用于安排流动游客。

组委会请求DR.Kong来完成这个任务,对这些定单进行合理安排,使得满足这些定单要求的房间数最少。

假设:某个定单上的游客一旦被安排到某房间,在他预定住宿的期间内是不换房间的。为了简化描述,定单上的开始住宿时间为距离现在的第几天。例如,定单为(10,30,5)表示游客要求使用10个房间,第30天开始连住5天。

输入描述:

第一行:T 表示有T组测试数据 每组测试数据第一行:N 表示定单数 每组测试数据接下来有N行,每行有三个整数 A B C 表示房间数,开始住宿时间和天数 1<=T<=100 1<=N<=10000 1<=A<=10 1<=B<=180 1<=c<=10

输出描述:

输出一个整数,为满足所有定单要求的最少房间数。

样例输入:

复制
1
3
3 10 4
4 9 3
3 12 6

样例输出:

7

题解:定向思维了,开始以为是稍微复杂的贪心。其实只要在对应的区间进行叠加。

#include <iostream>
using namespace std;
int s[200];
int main ()
{
    int t;
    cin>>t;
    while(t--)
    {
        fill(s,s+200,0);
        int n,maxx=-1;
        cin>>n;
        for (int i=0;i<n;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            for (int j=b;j<b+c;j++)
            {
                s[j]+=a;
                if(s[j]>maxx)
                    maxx=s[j];
            }
        }
        cout<<maxx<<endl;
    }
    return 0;
}

311-完全背包


内存限制:64MB  时间限制:4000ms  Special Judge: No

accepted:1  submit:1

题目描述:

直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。本题要求是背包恰好装满背包时,求出最大价值总和是多少。如果不能恰好装满背包,输出NO

输入描述:

第一行: N 表示有多少组测试数据(N<7)。 接下来每组测试数据的第一行有两个整数M,V。 M表示物品种类的数目,V表示背包的总容量。(0<M<=2000,0<V<=50000) 接下来的M行每行有两个整数c,w分别表示每种物品的重量和价值(0<c<100000,0<w<100000)

输出描述:

对应每组测试数据输出结果(如果能恰好装满背包,输出装满背包时背包内物品的最大价值总和。 如果不能恰好装满背包,输出NO)

样例输入:

复制
2
1 5
2 2
2 5
2 2
5 1

样例输出:

NO
1

题解:模板,由于要刚好装满,所以除dp[0]=0,其他的都赋值很小的负数。

#include <iostream>
using namespace std;
int dp[50005],inf=0x3f3f3f3f;
int main ()
{
    int t;
    cin>>t;
    while(t--)
    {
        fill(dp,dp+50005,-inf);
        dp[0]=0;
        int m,V,w[2001],v[2001];
        cin>>m>>V;
        for (int i=0;i<m;i++)
            cin>>w[i]>>v[i];
        for (int i=0;i<m;i++)
            for (int j=w[i];j<=V;j++)
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        if(dp[V]>0)
            cout<<dp[V]<<endl;
        else
            cout<<"NO"<<endl;
    }
    return 0;
}

737-石子合并(一)


内存限制:64MB  时间限制:1000ms  Special Judge: No

accepted:1  submit:1

题目描述:

    有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

输入描述:

有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开

输出描述:

输出总代价的最小值,占单独的一行

样例输入:

复制
3
1 2 3
7
13 7 8 16 21 4 18

样例输出:

9
239

题解:dp[i][j]表示:i到j合并代价最小。dp[i][j]=min(dp[i][k]+dp[k+1][j]+i到j合并代价),i<=k<j;

#include <iostream>
using namespace std;
const int inf=0x3f3f3f3f;
int dp[201][201],sum[201];
int main ()
{
    int n,m;
    while(cin>>n)
    {
        sum[0]=0;
        for (int i=1; i<=n; i++)
        {
            cin>>m;
            sum[i]=sum[i-1]+m;
            dp[i][i]=0;//不能合并
        }
        for (int len=1; len<n; len++)
        {
            for (int i=1; i<=n-len; i++)
            {
                int j=i+len;
                dp[i][j]=inf;
                for (int k=i;k<j;k++)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
            }
        }
        cout<<dp[1][n]<<endl;
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值