2020-08-18

二分查找+DP

动态规划算法及经典例题: https://blog.csdn.net/zw6161080123/article/details/80639932.
(花时间掌握,上面这个博客对于动态规划的基本思想和转化方程讲述很详细,对动态规划的典型用法和例题也讲解十分清楚,对本次的题集有很大帮助)

A

题意

n个班每个班的学生报名ABC三种课程,求n个班中 max(只报名A的人+只报名B的人+只报名C+只报名AC+只报名BC+只报名AB+只报名ABC的人)数据有可能是假的,假的数据直接忽略掉,保证一定有一个真的数据

思路

报名abc的人数一定是只报名abc的人数,那么只报名ab的人数就是 报名ab人数 - 只报名abc人数,ac,bc同理
只报名a的人数一定是 报名a的人数- 只报名ac的人数-只报名ab的人数 - 只报名abc的人数,b,c同理

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=100007;
int n;
int main()
{
    int t;
    scanf("%d", &t);
    while (t > 0)
    {
        t--;
        scanf("%d", &n);
        int ans = -1;
        for (int i=0; i<n; i++)
        {
            int a, b, c, ab, ac, bc, abc;
            int a1, b1, c1, ab1, ac1, bc1, abc1;
            scanf("%d%d%d%d%d%d%d", &a,&b,&c,&ab,&bc,&ac,&abc);
            abc1 = abc;
            ab1 = ab - abc1;
            ac1 = ac - abc1;
            bc1 = bc - abc1;
            a1 = a - ab1 - ac1 - abc1;
            b1 = b - ab1 - bc1 - abc1;
            c1 = c - ac1 - bc1 - abc1;
            if (a1 < 0 || b1 < 0 || c1 < 0 || ab1 < 0 ||
                    bc1 < 0 || ac1 < 0 || abc1 < 0)//判断数据是否正确
            {
                continue;
            }
            ans = max(ans, a1+b1+c1+ab1+bc1+ac1+abc1);
        }
        cout << ans << endl;
    }
    return 0;
}

B

题意

用规定数目的猫粮换取最大数目的豆子,每个测试样例先给出规定的猫粮和房间数,后面给出每个房间的豆子和所需消耗的猫粮,直到输入-1 -1结束

思路

题目规定可以部分猫粮换取房间中的部分豆子,那么可以将每个房间的豆子和所需猫食做比例当成性价比,按性价比进行排序,按性价比排名依次获取豆子消耗猫粮,直到所剩猫粮无法完全获取某个房间内所有豆子时,则将所剩猫粮按比例换取相应豆子,求出最大豆子数

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=1007;
int M,N; //M-catfood N-room
struct node
{
    int j;  //javabean
    int f;  //cat food
    double rate;  //两者比例
} a[maxn];
double cmp(node a,node b)
{
    return a.rate>b.rate;
}
int main()
{
    double sum;
    while(cin>>M>>N)
    {
        sum=0;
        if(M==-1 && N==-1)
            break;
        for(int i=0; i<N; i++)
        {
            cin>>a[i].j>>a[i].f;
            a[i].rate=a[i].j*1.0/a[i].f*1.0;
        }
        sort(a,a+N,cmp);//C++sort排序,与C的快排相似
        for(int i=0; i<N; i++)
        {
            if(M>=a[i].f)
            {
                M-=a[i].f;
                sum+=a[i].j;
            }
            else if(M<a[i].f && M>0)
            {
                sum+=M*a[i].rate;
                break;
            }
        }
       printf("%.3lf\n",sum);
    }
    return 0;
}

C

题意

合理选择电视节目,使得可以观看最多的节目

思路

给出了每个节目的开始和结束时间,要求尽可能观看更多节目,可以把节目进行排序,按结束时间从小到大进行排序,当结束时间相等时,按开始时间由大到小排序(运用sort排序,sort排序使用方法自己掌握),再依次遍历,判断能否观看当前节目,并更新结束时间

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=107;
struct TV
{
    int start,endup;
}x[maxn];
bool cmp(const TV &p1,const TV &p2)
{
    if(p1.endup!=p2.endup)
        return p1.endup<p2.endup;
    else
        return p1.start>p2.start;
}
int main()
{
    int n;
    while(cin>>n)
    {
        int sum=1;
        if(n==0)
            break;
        for(int i=0;i<n;i++)
            cin>>x[i].start>>x[i].endup;
        sort(x,x+n,cmp);
        int t=x[0].endup;//第一个节目的结束时间
        for(int i=1;i<n;i++)
        {
            if(x[i].start>=t)//如果后一个节目的开始时间大于前面的结束时间,代表后一个节目可以观看
            {
                sum++;
                t=x[i].endup;//更新结束时间
            }
        }
        cout<<sum<<endl;
    }
    return 0;
}

D

题意

农夫有N间牛舍,牛舍排在一条线上,第i号牛舍在xi的位置。他的小牛彼此之间都会互相攻击。农夫为了防止牛之间互相伤害,决定要把每头牛都放在离其他牛尽可能远的牛舍。牛的数量是C,会给出n间牛舍的位置。

我们要使每两头牛的距离尽可能大,然后找出这些距离中的最小值。也就是离得最近的两头牛之间的距离。

思路

我们可以利用二分和贪心结合的思想,通过二分来猜测到一个值,然后进行判断,如果答案可行,则猜的距离可以再大些,如果答案不可行,则猜的距离就要再小些。在判断答案是否可行时,可以用cnt来记录匹配到槽的奶牛数,先将第一头奶牛放在第一个槽,然后依次枚举每个槽,当枚举的槽与前一个放奶牛的槽的距离>=猜的答案时,cnt++,最后如果cnt>=牛的数量,说明这个距离可行,如果cnt<牛的数量,说明这个距离太大了。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=100007;
int n,c;
long long a[maxn];//数据大,用 longlong
int check(long long mid)
{
    int cnt=1;      //记录牛的数量
    int crw=a[0];  //第一头牛肯定是放在最边上第一个位置。
    for(int i=1; i<n; i++)
    {
        if(a[i]-crw>=mid) //如果槽到前一头牛之间的距离大于mid,就可以将牛放到这个槽中
        {
            cnt++;
            crw=a[i];
        }
        if(cnt>=c)return 1;
    }
    return 0;
}
int main()
{
    cin>>n>>c;
    for(int i=0; i<n; i++)
        cin>>a[i];
    sort(a,a+n);
    long long l=a[0],r=a[n-1];
    while(r-l>1)//二分定距离
    {
        long long mid=(l+r)/2;
        if(check(mid))l=mid;//如果可以放牛的数量比给定的大,说明距离(mid)小了,需要放大
        else r=mid;//反之放小
    }
    cout<<l<<endl;
    return 0;
}

E

题意

有n段长度分别为Li的电缆,要求把它们分割成K条长度为X的电缆,问X的最大值为多少。

思路

将X视为变量,可知它的范围为0~max; 那么问题就变成了电缆长度取X时,所得的电缆条数大于,还是等于,或小于K的问题。 用二分查找法能很快的找出K的值,不过要注意精度(吐槽double的精度问题,实在是见鬼的存在),直接输出时会四舍五入,五入时的结果肯定是错的,注意向下取整。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=100007;
double a[maxn];
int n,k;
bool dix(double x)
{
    int num=0;
    for(int i=0; i<n; ++i)
        num+=(int)(a[i]/x);//取整相当于向下取整,如果四舍五入那么长度就会超出
    return num>=k;//满足条件是返回true,不满足返回false
}
int main()
{
    while(scanf("%d %d",&n,&k)!=EOF)
    {
        for(int i=0; i<n; ++i)
            scanf("%lf",&a[i]);
        double left=0,right=100001,mid;//题目规定最大长度为100公里
        int i=1000;//进行1000次二分查找
        while(i--)
        {
            mid=(left+right)*1.0/2;
            if(dix(mid))
                left=mid;
            else
                right=mid;
        }
        printf("%0.2f\n",floor(right*100)/100);//double的精度问题就跟鬼一样,实在不明白同意义的式子有不一样的结果
    }
    return 0;
}

F

题意

求函数的解

思路

f(x)求导可知在[0,1]上单调递减。所有有唯一解的充要条件是f(0)>0 且 f(1)<0。利用单调性,可以用二分解决。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const double eps = 1e-8;//定义一个趋于0的数,即足够小的正数
int p,q,r,s,t,u;
double f(double x)
{
    return exp(-x)*p+sin(x)*q+cos(x)*r+tan(x)*s+x*x*t+u;
    //使用的数学函数均返回浮点数,所以结果最接近0的解就是所求解
}
int main()
{
    while (scanf("%d%d%d%d%d%d",&p,&q,&r,&s,&t,&u)!=EOF)
    {
        if (f(0) < -eps || f(1) > eps)//单调递减函数没有唯一解的条件
        {
            cout<<"No solution"<<endl;
            continue;
        }
        double l = 0;
        double r = 1;
        double ff;
        double x;
        for (int i=0; i<100; i++)//100次二分查找
        {
            x = (l+r)/2.0;
            ff = f(x);
            if (ff < -eps)//根据单调递减函数图像判断X是放大还是缩小
                r = x;
            else if (ff > eps)
                l = x;
            else
                break;//若所取X带入函数结果趋于0,则为所求解
        }
        printf("%.4lf\n",x);
    }
    return 0;
}

G

题意

判断一组数是不是Jolly,即相邻数字之间差值的绝对值从1到n-1都有

思路

没有算法,直接简单模拟,数组a存放原始数据,数组b存放数组a相邻的元素的差值的绝对值,将b的值排一下序,看一下是不是所有的b[i]=i+1,所有的都符合输出Jolly else 输出Not Jolly

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=3007;
int a[maxn],b[maxn];
using namespace std;
int main()
{
    int n,flag;
    while(cin>>n)
    {
        for(int i=0; i<n; i++)
            cin>>a[i];
        for(int i=0; i<n-1; i++)
            b[i]=abs(a[i]-a[i+1]);
        sort(b,b+n-1);
        flag=0;
        for(int i=1; i<n; i++)
        {
            if(b[i-1]!=i)
            {
                flag=1;
                break;
            }
        }
        if(!flag)
            cout<<"Jolly"<<endl;
        else
            cout<<"Not jolly"<<endl;
    }
    return 0;
}

H

题意

有N头牛叠罗汉,对于每头牛定义一个难受值,这个值等于在它上面的所有奶牛的体重减去它的力量。让你求一种排序方法使所有牛的难受值最小,题目让我们输出这个排序中最大的一头牛的难受值。

思路

难点在于贪心的选择,可以试想,如果在底部的牛它的体重和力量越大,那么其他在上面的牛的总体重就越小,再减去底下牛的力量,因为力量大,所得难受值就越小了,所以按照每头牛的体重+力量这个值进行排序。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=100007;
struct node
{
    LL w;
    LL s;
} cow[maxn];
bool cmp(node a,node b)
{
    return a.s+a.w<b.s+b.w;
}
int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0; i<n; i++)
            cin>>cow[i].w>>cow[i].s;
        if(n==1)
        {
            cout<<-cow[0].s<<endl;    //n==1时直接输出负的牛的力量
            continue;
        }
        sort(cow,cow+n,cmp);
        LL t=0,res=-999999;//初始化一个很小的数
        for(int i=0; i<n; i++)
        {
            if(t-cow[i].s>res)  res=t-cow[i].s;  //找最大
            t+=cow[i].w;  //不断累积
        }
        cout<<res<<endl;
    }
    return 0;
}

I

题意

中文==人话,相信你们看得懂题

思路

动态规划中的回溯法,从倒数第二行开始更新,从底向上不断选择最优值,不断更新塔的数字,最终塔顶的数字就是最优值,更新方法可对着代码步骤在纸上模拟一下,回溯法主要是逆向思维,从底向上选择。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=107;
int main()
{
    int k;
    cin>>k;
    while(k--)
    {
        int m;
        cin>>m;
        int a[maxn][maxn];
        for(int i=0; i<m; i++)
        {
            for(int j=0; j<i+1; j++)
            {
                cin>>a[i][j];
            }
        }
        for(int i=m-2; i>=0; i--)
        {
            for(int j=0; j<=i; j++)
            {
                if(a[i+1][j]>=a[i+1][j+1])
                    a[i][j]+=a[i+1][j];
                else
                    a[i][j]+=a[i+1][j+1];
            }
        }
        cout<<a[0][0]<<endl;
    }
    return 0;
}

J

题意

一排富有的村子,一排贫穷的村子,然后对应的村之间修路,要求不交叉,最多能修多少条。

思路

这题很难想到是求最大递增子序列,可以按照贫穷村子的序号从小到大排序,然后找富有村子序列的最长上升子序列就可以了,因为当贫穷村子按顺序排好依次修好与对应富村的路时,排在后面的贫穷村子只能选择比前面序号要大的富村,这样就能保证不相交,而且最大递增子序列的长度就是最优解。最长上升子序列的n×logn算法就是,来一个数,比序列末尾大就直接加入队尾,否则从队列中找到第一个比当前数大的,用当前数将其替换,最后队列的长度就是解。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=500007;
struct node
{
    int s,e;
} a[maxn];
int len,n;
int dp[maxn];//记录最长上升子序列
int cmp(node a,node b)
{
    return a.s<b.s;
}
void LIS()
{
    memset(dp,0,sizeof(dp));
    int l,r;
    len=1;//初始最长上升子序列长度默认为1
    dp[1]=a[1].e;
    for (int i=2; i<=n; i++)
    {
        //二分查找dp中第一个比当前数还大的数,避免超时,未找到则添加到队尾
        l=1,r=len;
        while (l<=r)
        {
            int mid=(l+r)>>1;//位运算,向右一位相当于除以2
            if (a[i].e>dp[mid])
                l=mid+1;
            else
                r=mid-1;
        }
        dp[l]=a[i].e;
        if (l>len)
            len++;
    }
}
int main()
{
    int t=1;
    while (scanf("%d",&n)!=EOF)
    {
        memset(a,0,sizeof(a));
        for (int i=1; i<=n; i++)//数组下标从1开始
            cin>>a[i].s>>a[i].e;
        sort(a+1,a+n+1,cmp);
        LIS();
        printf("Case %d:\n",t++);
        if (len==1)//输出单词road单复数也是坑哦
            printf("My king, at most %d road can be built.\n\n",len);
        else
            printf("My king, at most %d roads can be built.\n\n",len);
    }
    return 0;
}

K

题意

这个游戏可以由两个或两个以上的玩家玩。它由一个棋盘和一些棋子组成,所有的棋子都用一个正整数来标记。玩家从起点开始,最后必须跳到终点。在跳跃的过程中,玩家会访问路径中的棋子,但是每个人都必须从一个棋子跳到另一个绝对大的棋子。所有的玩家都不能倒退。一跳可以从一个棋子跳到下一个,也可以跨越多个棋子。注意,你的分数来自于你跳跃路径上的棋子值的总和。您的任务是根据给定的棋子列表输出最大值。

思路

题目一看就是求递增子序列的最大和,动态转化方程不理解的可以参考顶上的博客。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=2007;
LL a[maxn],b[maxn],dp[maxn];//虽然每个数字都在int范围内,但求和后可能会溢出,所以设为LL
//dp[i]表示序列到下标i为止的递增子序列最大和
int main()
{
    int n;
    while(cin>>n)
    {
        if(n==0)
            break;
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        for(int i=0; i<n; i++)
            cin>>a[i];
        LL ans = -99999;//初始化一个很小的数求最大值
        for(int i=0; i<n; i++)
        {
            dp[i] = a[i];
            for(int j=0; j<i; j++)
            {
                if(a[j]<a[i])
                    dp[i] = max(dp[j]+a[i], dp[i]);//动态转化方程
            }
            ans = max(ans,dp[i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}

L

题意

题目标题即所求,求最长公共子序列的长度。

思路

具体思想和思路解法参考顶上的博客,代码供参考。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=1007;
int f[maxn][maxn];
char s1[maxn], s2[maxn];
int len1, len2;
int main()
{
    while(scanf("%s %s", s1, s2) != EOF)
    {
        len1 = strlen(s1);
        len2 = strlen(s2);
        memset(f, 0, sizeof(f));//初始化
        for(int i = 1; i <= len1; i++)
        {
            for(int j = 1; j <= len2; j++)
            {
                if(s1[i-1] == s2[j-1])
                    f[i][j] = f[i-1][j-1]+1;
                else
                    f[i][j] = max(f[i-1][j], f[i][j-1]);
            }
        }
        cout << f[len1][len2] << endl;
    }
    return 0;
}

M

题意

01背包问题模型,给定物品个数和背包体积,给出每个物品的价值和放进背包所要消耗的体积,对于每个物品只能选择要与不要,所以叫01背包,要求合理选择物品,在不超出背包体积下,使背包内物品的总价值最大。

思路

参考顶上博客对于背包问题的解法,详细易懂,小小菜鸡就不在大佬面前班门弄斧了。(优化后用一维数组的代码更加简单易懂,博客中也有提到,这里使用二维数组,掌握后更能很好地理解优化后的代码)

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=1007;
struct bone
{
    int value,cost;
};
bone x[maxn];
int dp[maxn][maxn];
int main()
{
    int k;
    cin>>k;
    while(k--)
    {
        int n,v;
        cin>>n>>v;
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=n; i++)
            cin>>x[i].value;
        for(int i=1; i<=n; i++)
            cin>>x[i].cost;
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<=v; j++)
            {
                if(j>=x[i].cost)
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-x[i].cost]+x[i].value);
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        cout<<dp[n][v]<<endl;
    }
    return 0;
}

N

题意

与上一道题类似,只不过多了物品的种类和每个种类对应的物品数。

思路

只是给物品分了类,同种类的物品属性是一样的,还是背包放物品的问题,在上一道题的基础上进行变换即可,这里是优化后用一维数组表示的代码。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=107;
struct rice
{
    int value,weight,num;
};
int main()
{
    int k;
    cin>>k;
    rice x[maxn];
    int dp[maxn];
    while(k--)
    {
        int money,kind;
        cin>>money>>kind;
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=kind; i++)
            cin>>x[i].value>>x[i].weight>>x[i].num;
        for(int i=1; i<=kind; i++)
        {
            for(int j=1; j<=x[i].num; j++)
            {
                for(int l=money; l>=x[i].value; l--)
                {
                    dp[l]=max(dp[l],dp[l-x[i].value]+x[i].weight);
                }
            }
        }
        cout<<dp[money]<<endl;
    }
    return 0;
}~

O

自己掌握完全背包和多重背包,以及01背包二进制优化
推荐博客:1、https://blog.csdn.net/qq_38984851/article/details/81133840.
2、https://blog.csdn.net/qq_41286356/article/details/106882943?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.nonecase.
掌握了解后再自己解决本题。

P

解题思路很详细的博客链接: https://blog.csdn.net/liangbopirates/article/details/9632829.

Q

题意

研究人员有n种类型的砖块,每种类型的砖块都有无限个。第i块砖块的长宽高分别用xi,yi,zi来表示。 同时,由于砖块是可以旋转的,每个砖块的3条边可以组成6种不同的长宽高。在构建塔时,当且仅当A砖块的长和宽都分别小于B砖块的长和宽时,A砖块才能放到B砖块的上面,因为必须留有一些空间让猴子来踩,
你的任务是编写一个程序,计算猴子们最高可以堆出的砖块们的高度。

思路

1.先列出所有长方体的情况,每种长方体有6种摆法。

2.将所有长方体按先长后宽由大到小进行排序,以便进行顺序堆叠。

3.然后j到i之间的长方体进行堆叠(遍历了所有可堆叠的情况),记录下最大的高度,

4.每i层之前的最大高度加上本层高度作为下次比较的高度,并记录下来。

5.输出所有堆叠高度里面最高的.

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=207;
struct node
{
    int x,y,h;
} a[maxn];
int ant;
bool cmp(node a,node b)
{
    //几种积木长宽高的各种情况进行排序,当长不同时,按长从大到小排列;相同时,按宽从大到小排列
    if (a.x == b.x)
        return a.y > b.y;
    return a.x > b.x;
}
void PushUp(int t1,int t2,int t3)
{
    //列出同种积木长宽高的各种情况,一种积木长宽高有6种情况
    a[ant].h = t3;
    a[ant].x = t1;
    a[ant].y = t2;
    ant++;
    a[ant].h = t3;
    a[ant].x = t2;
    a[ant].y = t1;
    ant++;
    a[ant].h = t2;
    a[ant].x = t1;
    a[ant].y = t3;
    ant++;
    a[ant].h = t2;
    a[ant].x = t3;
    a[ant].y = t1;
    ant++;
    a[ant].h = t1;
    a[ant].x = t2;
    a[ant].y = t3;
    ant++;
    a[ant].h = t1;
    a[ant].x = t3;
    a[ant].y = t2;
    ant++;
}
int main()
{
    int n;
    int ans;
    int dp[maxn];
    int Case = 1;
    while (scanf ("%d",&n)!=EOF)
    {
        if(n==0)
            break;
        ant = 1;
        for (int i = 1 ; i <= n ; i++)
        {
            int t1,t2,t3;
            scanf ("%d %d %d",&t1,&t2,&t3);
            PushUp(t1,t2,t3);
        }
        ant--;
        printf ("Case %d: maximum height = ",Case++);
        sort (a+1, a+1+ant, cmp);
        ans = 0;
        for (int i = 1 ; i <= ant ; i++)
        {
            dp[i] = a[i].h;
            for (int j = i - 1 ; j >= 1 ; j--)
            {
                if (a[i].x < a[j].x && a[i].y < a[j].y)
                    dp[i] = max (dp[i], dp[j] + a[i].h);
            }
            ans = max (ans, dp[i]);
        }
        printf ("%d\n",ans);
    }
    return 0;
}

R

题意

求N的阶乘,很明显当N很大时,结果会溢出,会显示乱码。

思路

可将结果用数组表示,数组的长度就是结果的位数,数组元素的值就是对应结果每一位的数字,求出结果也只需要模拟我们平常在草稿纸上用竖式算乘法的过程。

代码

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=50007;
int main()
{
    int n;
    int a[maxn];
    while(scanf("%d",&n)!=EOF)
    {
        memset(a,0,sizeof(a));
        a[0]=1;
        int cnt=1;
        for(int i=1; i<=n; i++)
        {
            int k=0;
            for(int j=0; j<cnt; j++)
            {
                //模拟竖式乘法计算
                int tmp=a[j]*i+k;
                a[j]=tmp%10;
                k=tmp/10;
            }
            //当最后一位乘完还有进位时,代表计算结果多出一位,那么数组长度增加
            while(k)
            {
                a[cnt++]=k%10;
                k/=10;
            }
        }
        for(int i=cnt-1; i>=0; i--)
        {
            if(i==0)
                printf("%d\n",a[i]);
            else
                printf("%d",a[i]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值