NUC-ACM/ICPC 寒假训练 简单DP A - G题

第一题:数塔 HDU - 2084

做法:

从第 i , j 个 节点往下走的最优解可以由从第 i+1,j 个节点往下走的最优解和第i+1,j+1个节点往下走的最优解得出,二者取其优即可。

代码:

记忆化搜素

 1 #include<iostream>
 2 using namespace std;
 3 #include<cstdio>
 4 using namespace std;
 5 int n;
 6 int f[100][100];
 7 int v[100][100];
 8 int a[100][100];
 9 int fget(int x,int y)//记忆化搜索求值
10 {
11     if (x<=0||y<=0) return 0;//如果超出范围返回0
12     if (x>n||y>x) return 0;//如果超出范围返回0
13     if (v[x][y]) return f[x][y];//如果之前访问过那么直接返回之前访问过的值
14     v[x][y]=1;//标记访问
15     int max=-10000000;//max为最大值 初始化一个很小的数
16     if (!v[x+1][y])//如果之前没有递归过 x+1,y 的 值
17     {
18         f[x+1][y]=fget(x+1,y);//递归获得答案
19     }
20     if (!v[x+1][y+1])//如果之前没有递归过 x+1,y+1 的 值
21     {
22         f[x+1][y+1]=fget(x+1,y+1);//递归获得答案
23     }
24
25     //f[x][y]的值可以由 f[x+1][y] + a[x][y]得出
26     f[x][y]=f[x+1][y]+a[x][y];
27
28     //当然, f[x][y]的值也可以由 f[x+1][y+1] + a[x][y]得出
29     //那么我们就取两者中较大的那个
30     if (f[x][y]<=f[x+1][y+1]+a[x][y]) f[x][y]=f[x+1][y+1]+a[x][y];
31
32     return f[x][y];//返回答案
33 }
34 void deal()
35 {
36     int i;
37     int j;
38     cin>>n;
39     //读取 + 初始化
40     for (i=1;i<=n;i++)
41         for (j=1;j<=i;j++)
42         {
43             f[i][j]=0;
44             scanf("%d",&a[i][j]);
45             v[i][j]=0;
46             f[i][j]=a[i][j];
47         }
48     int max=-1;
49
50     //fget(1,1)也就是数组顶部的元素也就是答案
51     cout<<fget(1,1)<<endl;
52
53 }
54 int main()
55 {
56     int n;
57     cin>>n;//n组数据
58     while (n--)
59     {
60         deal();//逐个处理
61     }
62     return 0;
63 }

递推代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5
 6 int main() {
 7     int a[105][105];
 8     int t, n, i, j;
 9     while(cin>>t) {
10         while(t--) {
11             cin>>n;
12             memset(a,0,sizeof(a));
13             for(i=0; i<n; i++)
14                 for(j=0; j<=i; j++)
15                     cin>>a[i][j];
16             for(i=n-1; i>=0; i--)
17                 for(j=0; j<=i; j++)
18                     a[i][j]=a[i][j]+max(a[i+1][j],a[i+1][j+1]);
19             cout<<a[0][0]<<endl;
20         }
21     }
22     return 0;
23 }

第二题:超级楼梯

来源:HDU - 2041

转态转移方程 f[i]=f[i-1]+f[i-2]

代码:

记忆化搜索

 1 #include<iostream>
 2 using namespace std;
 3 #include<cstring>
 4 #include<cstdio>
 5 int dp[50];
 6 int dfs(int x){//递归得到x级楼梯的走法数量
 7     if(x<0) return 0;//小于0级台阶当然是0
 8     if(dp[x]>0)//已经有答案
 9         return dp[x];
10     else
11         //该级台阶的答案可以由i-1阶台阶的答案和i-2阶的答案求和得到
12         return dp[x]=dfs(x-1)+dfs(x-2);
13 }
14 int main(){
15     int t;
16     memset(dp,0,sizeof(dp));
17     dp[0]=1;
18     scanf("%d",&t);
19     while(t--){
20         int d;
21         scanf("%d",&d);
22         printf("%d\n",dfs(d-1));
23     }
24     return 0;
25 }

递推:

 1 #include<iostream>
 2  using namespace std;
 3  int main(){
 4     int n;
 5     cin>>n;
 6         for(int j=0;j<n;j++){
 7             int m;
 8             cin>>m;
 9             int a[45];
10             for(int i=2;i<=m;i++){
11                 a[2]=1;
12                 a[3]=2;
13                 if(i>3)
14                 a[i]=a[i-1]+a[i-2];
15             }
16             cout<<a[m]<<endl;
17         }
18  }

第三题

母牛的故事

来源:HDU - 2018

第一年只有一开始的牛

第二年-第四年只有一开始的牛生了小牛

第五年第二年出生的小牛也生了小牛

第六年第三年出生的小牛也可以生母牛了

也就是说第i年时,3年前出生(包括第 前三年)的母牛 都有生育能力

那么设dp[i]为第i年的牛的数量

那么,显然dp[i-1]为没生小牛之前的牛的数量

dp[i-3]为3年前的牛的数量,也就是有生育能力的小牛的数量

那么状态转移方程就可以得到:

dp[i]=dp[i-1]+dp[i-3]

代码:

 1 #include<iostream>
 2 using namespace std;
 3 #include<cstdio>
 4 #include<cstring>
 5 int f[60];//第n年牛的数量
 6 int main(){
 7     for(int i=1;i<=4;i++)
 8         f[i]=i;
 9
10     //f[i-1]为去年牛的数量 f[i-3]为可以生牛的母牛的数量 也是新生的小牛的数量
11     for(int i=5;i<=55;i++)
12         f[i]=f[i-1]+f[i-3];
13     int n;
14     while(scanf("%d",&n)!=EOF&&n){//循环读入直到文件尾部或者n为0时结束
15         printf("%d\n",f[n]);
16     }
17     return 0;
18 }

第四题:

Bone Collector

来源:

HDU - 2602

题目大意:

已知N个糖果的重量和价值. 我们有一个口袋, 最多可以装V重量的糖果. 问口袋最多能放多少价值的糖果进去? Input

输入的第一行是T, 表示有T组数据.

每组数据由三行组成.

第一行包含两个整数N和V(N <= 1000, V <= 1000). N表示糖果的个数, V表示口袋的载重.

第二行包含N个整数, 表示每一颗糖果的价值.

第三行包含N个整数, 表示每一颗糖果的重量.

Output

对每一组数据, 输出口袋最终可以放进去糖果的价值.

Sample Input

1
5 10
1 2 3 4 5
5 4 3 2 1

Sample Output

14

做法:一个简单的01背包问题解决0 1 背包问题的思想看背包九讲题解参见代码注释
 1 #include<iostream>
 2 using namespace std;
 3 #include<cstring>
 4 #include<cstdio>
 5 void deal();
 6 int main(){
 7     int t;
 8     scanf("%d",&t);
 9     while(t--)//t组数据
10         deal();
11     return 0;
12 }
13 int *value,*weight,*f;
14 void deal(){
15     int N,V;
16     scanf("%d%d",&N,&V);
17     value=new int[N];//每个糖果重量数组
18     weight=new int[N];//每个糖果价值数组
19     for(int i=0;i<N;i++)
20         scanf("%d",value+i);
21     for(int i=0;i<N;i++)
22         scanf("%d",weight+i);
23     f=new int[V+1];
24     memset(f,0,sizeof(int)*(V+1));//初始化
25     for(int i=0;i<N;i++)//对于第i件物品
26         for(int j=V;j>=0;j--)//当空间为j的时候
27             if(j-weight[i]<0)//如果空间j放不下第i件物品
28                 continue;//那就不管它好了
29             else
30             f[j]=max(f[j],f[j-weight[i]]+value[i]);//要么把f[j]本身和放下第i件物品比一下看哪个大
31     int ans=0;
32     for(int i=0;i<=V;i++)
33         ans=max(ans,f[i]);//答案取最大的那个
34     printf("%d\n",ans);
35     return;
36 }

第五题:

Common Subsequence

HDU - 1159

题解:

第一个字符串的前 i 个字符 和 第二个字符串 的 前 j 个字符 的最长公共子串 可以由

第一个字符串的前 i-1 个字符 和 第二个字符串的前j个字符 的最长公共子串

    第一个字符串的前 i 个字符 和 第二个字符串的前j-1个字符 的最长公共子串

    如果第一个字符串的第i个字符和第二个字符串的第j个字符相同

第一个字符串的前 i 个字符 和 第二个字符串 的 前 j 个字符 的最长公共子串 也可以由

     第一个字符串的前i-1个字符和第二个字符的前j-1个字符的最长公共子串的得到

  代码:记忆化搜索

 1 #include<iostream>
 2 using namespace std;
 3 #include<cstdio>
 4 #include<cstring>
 5 const int maxn=1010;
 6 char *s1,*s2;
 7 int **dp;
 8
 9 int dfs(int x,int y){//求s1的前x个字符与s2的前y个字符
10     int ans=0;
11     if(x<0||y<0)//如果x或y中有一个超出字符串边界
12         return 0; //那么返回0
13     if(dp[x][y]>=0)//如果之前之前已经递归过此种状态,直接返回
14         return dp[x][y];
15     if(s1[x]==s2[y])//如果s1的第x个字符与s2的第y个字符相等
16     ans=max(dfs(x-1,y-1)+1,ans);
17     ans=max(ans,dfs(x-1,y));//递归查找求s1的前x-1个字符与s2的前y个字符
18     ans=max(ans,dfs(x,y-1));//递归查找求s1的前x个字符与s2的前y-1个字符
19     return dp[x][y]=ans;//记录此状态的值并返回
20 }
21
22 int main(){
23     s1=new char[maxn];//声明内存
24     s2=new char[maxn];
25     while(scanf("%s%s",s1,s2)!=EOF){
26
27         int len1,len2;
28         len1=strlen(s1);//获取字符串长度
29         len2=strlen(s2);
30
31         //声明数组
32         dp=new int*[len1+1];
33         for(int i=0;i<=len1;i++)
34             dp[i]=new int[len2+1];
35
36         //初始化数组
37         for(int i=0;i<=len1;i++)
38             for(int j=0;j<=len2;j++)
39                 dp[i][j]=-1;
40
41         printf("%d\n",dfs(len1-1,len2-1));
42
43         //释放内存
44         for(int i=0;i<=len1;i++)
45             delete dp[i];
46         delete dp;
47         delete s1;
48         delete s2;
49
50         s1=new char[maxn];//重新声明
51         s2=new char[maxn];
52     }
53     return 0;}

递推:

#include <iostream>
#include<cstring>
using namespace std;
int dp[2001][2001];
int main() {
    char a[2001],b[2001];
    while(cin>>a>>b) {
        int i,j;
        int al,bl;
        al=strlen(a);
        bl=strlen(b);
        for(i=0; i<=al; i++)
            dp[i][0]=0;
        for(i=0; i<=bl; i++)
            dp[0][i]=0;
        for(i=1; i<=al; i++)
            for(j=1; j<=bl; j++) {
                if(a[i-1]==b[j-1])
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j] = dp[i-1][j] > dp[i][j-1] ? dp[i-1][j] : dp[i][j-1];
            }
        cout<<dp[al][bl]<<endl;
    }
    return 0;
}

第六题:

最少拦截系统

HDU - 1257

一种思路是

贪心去求解

把每一个导弹和它之前发射的所有子弹的拦截系统的高度去比较

如果有比它高的,那么贪心地去选择最低的那个可行的拦截系统

如果没有比它高的拦截系统,新建一个拦截系统答案+1

第二种思路就是把问题转化为最长递增子序列的问题

和第K题解法相同

第七题:

Piggy-Bank

来源: HDU - 1114

题目大意:

在 ACM 能够开展之前,必须准备预算,并获得必要的财力支持。该活动的主要收入来自于 Irreversibly Bound Money (IBM)。思路很简单。任何时候,某位 ACM 会员有少量的钱时,他将所有的硬币投入到小猪储钱罐中。这个过程不可逆,因为只有把小猪储钱罐打碎才能取出硬币。在足够长的时间之后,小猪储钱罐中有了足够的现金,用于支付 ACM 活动所需的花费。

但是,小猪储钱罐存在一个大的问题,即无法确定其中有多少钱。因此,我们可能在打碎小猪储钱罐之后,发现里面的钱不够。显然,我们希望避免这种不愉快的情况。唯一的可能是,称一下小猪储钱罐的重量,并尝试猜测里面的有多少硬币。假定我们能够精确判断小猪储钱罐的重量,并且我们也知道给定币种的所有硬币的重量。那么,我们可以保证小猪储钱罐中最少有多少钱。

你的任务是找出最差的情形,即判断小猪储钱罐中的硬币最少有多少钱。我们需要你的帮助。不能再贸然打碎小猪储钱罐了!

输入

输入包含 T 组测试数据。输入文件的第一行,给出了 T 的值。

对于每组测试数据,第一行包含 E 和 F 两个整数,它们表示空的小猪储钱罐的重量,以及装有硬币的小猪储钱罐的重量。两个重量的计量单位都是 g (克)。小猪储钱罐的重量不会超过 10 kg (千克),即 1 <= E <= F <= 10000 。每组测试数据的第二行,有一个整数 N (1 <= N <= 500),提供了给定币种的不同硬币有多少种。接下来的 N 行,每行指定一种硬币类型,每行包含两个整数 P 和 W (1 <= P <= 50000,1 <= W <=10000)。P 是硬币的金额 (货币计量单位);W 是它的重量,以 g (克) 为计量单位。

输出

对于每组测试数据,打印一行输出。每行必须包含句子 “The minimum amount of money in the piggy-bank is X.” 其中,X 表示对于给定总重量的硬币,所能得到的最少金额。如果无法恰好得到给定的重量,则打印一行 “This is impossible.” 。

解法:

一个完全背包问题,每个物品有无限种,问最优解

参见背包九讲,完全背包问题

代码:

 1 #include<iostream>
 2 using namespace std;
 3 #include<cstdio>
 4 int f[101000],a[10000],v[10000];
 5 int main()
 6 {
 7     int n;
 8     int t;
 9     cin>>t;
10     int l,r,fv;
11     while(t--)
12     {
13         scanf("%d%d",&l,&r);
14         fv=r-l;
15         cin>>n;
16         for (int i=1;i<=n;i++)
17             scanf("%d%d",&a[i],&v[i]);
18         for (int i=1;i<=fv;i++)//f[i]储存 总共i大小的重量 的 最小金额
19             f[i]=0x7fffffff/2;    //初始值赋个很大的值
20         f[0]=0;
21         for (int i=1;i<=n;i++)//对于第i种硬币
22         {
23             for (int j=v[i];j<=fv;j++)    //对于j大小的空间
24             {
25                 f[j]=min(f[j],f[j-v[i]]+a[i]);    //f[j]的值为f[j]和f[j-v[i]]较小的那个值
26             }
27         }
28         if (f[fv]==0x7fffffff/2) //如果依然是最大值,那么就是不可能
29             cout<<"This is impossible."<<endl;
30         else    //否则输出答案
31             printf("The minimum amount of money in the piggy-bank is %d.\n",f[fv]);
32     }
33     return 0;
34 } 

原文地址:https://www.cnblogs.com/xfww/p/8323738.html

时间: 2024-11-08 20:15:01

NUC-ACM/ICPC 寒假训练 简单DP A - G题的相关文章

ACM/ICPC算法训练 之 数学很重要-浅谈“排列计数” (DP题-POJ1037)

这一题是最近在看Coursera的<算法与设计>的公开课时看到的一道较难的DP例题,之所以写下来,一方面是因为DP的状态我想了很久才想明白,所以借此记录,另一方面是看到这一题有运用到 排列计数 的方法,虽然排列计数的思路简单,但却是算法中一个数学优化的点睛之笔. Poj1037  A decorative fence 题意:有K组数据(1~100),每组数据给出总木棒数N(1~20)和一个排列数C(64位整型范围内),N个木棒长度各异,按照以下条件排列,并将所有可能结果进行字典序排序 1.每一

POJ 4979 海贼王之伟大航路 【状压dp】【北大ACM/ICPC竞赛训练】

该死的题让我想起来艾斯之死... 首先想到dp(i)代表从1到[i表示的这些岛屿]所花的最小时间,然后每次枚举最后一个岛屿以此缩小范围,但发现枚举了最后一个岛屿后没有办法转移,因为不知道倒数第二个岛屿是什么,随着倒数第二个岛屿的不同,时间的增加也会不同,也就是不具备[无后效性]. 因此想到再加一个参数去约束当前问题的状态,dp(i,j)代表1到[i代表的这些点]所需的最少时间,且这趟旅程的最后一个岛屿是j,这样就可转移了,每次枚举倒数第二岛屿:答案就是dp( (1<<n) -1,n ) 具体的

POJ 8471 切割回文 【dp】【北大ACM/ICPC竞赛训练】

1 #include<iostream> 2 #include<vector> 3 #define INF 100000 4 using namespace std; 5 6 string s; 7 char a[1005]; 8 vector<int> hui[1005];//hui[i]里的k指 k到i组成回文 9 int dp[1005];//dp[i]代表前i个字符要切几刀 10 11 int main(){ 12 int t; cin>>t; 13

POJ 1194 Zipper 【dp】【北大ACM/ICPC竞赛训练】

现在做dp题有点感觉了,以前估计会觉得这题很难,现在觉得这是道不是很水的水题 dp[i][j]代表A里前i个加上B里前j个能不能组成C的前i+j个. 至于怎么想到的呢,n是200,有1000组数据,所以猜测要dp二维,然后很容易就想到了. 这里再考虑到C[i+j]的这个元素肯定是由A[i]或B[j]组成(即A的最末尾或B的最末尾,因为要保证原来的order) 所以这就变成了dp[i][j] = dp[i-1][j] | dp[i][j-1]  (当然这是A[i]=B[j]=C[i+j])的情况.

ACM/ICPC算法训练 之 数学问题

好歹我是数学专业的学生,还是要写写训练的时候遇到的数学问题滴~~ 在ACM集训的时候在各高校OJ上也遇见过挺多的数学问题,例如大数的处理,素数的各种算法,几何问题,函数问题(单调,周期等性质),甚至是各种数学定理或公式的变形.其实算法本身也属于数学研究的范畴(计算机本就是数学的衍生嘛),诸如众多排序算法,搜索算法也是许多精巧的数学逻辑思维,所以大家学计算机千万别忽视数学这门基础学科啊. 貌似说了好多废话= =||,待小编进入正题,今天在湖大OJ训练赛上看到一道数学题,话不多说,直接上图! ___

ACM/ICPC 算法训练 之 &quot;打表&quot;思路(防超时) ——附加素数筛选法

何为"打表"呢,说得简单点就是: 有时候与其重复运行同样的算法得出答案,还不如直接用算法把这组数据所有可能的答案都枚举出来存到一个足够大的容器中去-例如数组(打表),然后再输入数据的时候,直接遍历容器,检索这个数据是否有题意要求的结果. 举一个几乎所有程序员都知道的简单例子= =: 求素数(POJ 1595)-Prime cuts 这一题大意是给出 多组N(1~1000)和C,让你从N内素数的中间项向外扩展C个素数,比如给出7 1,素数有5个(注意此题出题人坑爹得让1作为"素

ACM/ICPC算法训练 之 BFS-广搜进阶-八数码(经典)(POJ1077+HDU1043)

八数码问题也称为九宫问题.(本想查查历史,结果发现居然没有词条= =,所谓的历史也就不了了之了) 在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同.棋盘上还有一个空格,与空格相邻的棋子可以移到空格中.要求解决的问题是: 给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤. 所谓问题的一个状态就是棋子在棋盘上的一种摆法.棋子移动后,状态就会发生改变.解八数码问题就是找出从初状态到目标状态所经过的一系列中间状态.八数码问题一

ACM/ICPC算法训练 之 BFS-广搜入+队列入门-抓牛(POJ3278)

这一题是练习广度优先搜索很好的例题,在很多广搜教学中经常用到,放在这里供学习搜索算法的孩纸们看看= = 题目大意:一维数轴上,农夫在N点,牛在K点,假定牛不会移动,农夫要找到这头牛只能够进行以下三种移动方法 2*N-跳跃到两倍于自己所在的位置 N+1 -右移一位 N -1 -左移一位 BFS解法解析:按照移动步数依次遍历队首,pop掉,并拓展出下一步所有可走位置并依次压入队列(不懂的孩纸们找度娘,很基础的数据结构)中,在这里为Code方便,我使用STL中的queue. Code如下: 1 //从

ACM/ICPC算法训练 之 数学很重要-平面几何(POJ 1269)

题意:给定四点的坐标(x,y),分别确定两直线,求出其交点,若重合or平行则输出相应信息 用四个点的坐标算出直线通式(ax+by+c=0)中的a,b,c,然后利用a,b,c计算出交点坐标(其他公式不够通用= =,比如斜率限制) 我利用两次平行判定重合 公式利用 初高中数学知识或代数知识 在草纸上仔细推导出来= =,让a,b,c为整数,比如可以演算得到a = y2-y1,b = x1-x2这一类公式. 详细Code如下: 1 //给定四点,分别确定两直线,求出其交点,若重合or平行则输出相应信息