2021-10-10训练总结

本文介绍了四个使用贪心算法和模拟技术解决计算机科学问题的例子,包括旅行家的预算优化、国王游戏中的高精度计算、约瑟夫环的递归求解以及雷涛的小猫问题的动态规划。这些实例展示了如何利用贪心策略在有限的计算资源下找到近似最优解,以及如何通过模拟和递归解决复杂环状结构的问题。
摘要由CSDN通过智能技术生成

1、旅行家的预算(贪心+模拟)

我最开始的思路是按照价格去排序,但是这样就会把线性的结构分成好几段,处理起来很麻烦。正确的做法是按照距离排序,用当前加油站的价格与此加油站之后在最大距离之内油价最小的加油站去比较。分别判断两次,算出所花的钱数,然后把当前加油站作为起点,继续进行此过程。

#include <bits/stdc++.h>

using namespace std;

struct s
{
    double d,p;
}x[100];
bool cmp(s x,s y)
{
    return x.d<y.d;
}
double d1,c,d2,p;
int n;
double max_dis;
 double sum=0;
void solve(int node,double rem_dis)
{
    double minp=1000;
    int min_node;
    for(int a=node+1;a<=n;a++)
    {
        if(x[node].d+max_dis<x[a].d)
            break;
        if(minp>x[a].p)
        {
            minp=x[a].p;
            min_node=a;
        }
    }
    if(x[node].p<=minp&&x[node].d+max_dis>=d1)
    {
        sum+=((d1-x[node].d)/d2-rem_dis)*x[node].p;
        cout<<fixed<<setprecision(2)<<sum<<endl;
        return;
    }
    if(minp==1000)
    {
        cout<<"No Solution"<<endl;
        return;
    }
    if(x[node].p<minp)
    {
        sum+=(c-rem_dis)*x[node].p;
        rem_dis=c-(x[min_node].d-x[node].d)/d2;
    }
    else
    {
        sum+=((x[min_node].d-x[node].d)/d2-rem_dis)*x[node].p;
        rem_dis=0;
    }
    solve(min_node,rem_dis);

}
int main()
{
    cin>>d1>>c>>d2>>p>>n;
    x[0].d=0;
    x[0].p=p;
    for(int a=1;a<=n;a++)
    {
        cin>>x[a].d>>x[a].p;
    }
    max_dis=c*d2;
    sort(x,x+n+1,cmp);
    solve(0,0);
    return 0;
}


2、国王游戏(贪心+高精)

这道题如果单纯是贪心的话,也属于一种题型——x.ax.b>y.ay.b。所以首先要根据题目判断出来这一公式,这是第一步。对于贪心的题目,如果结构体里面包含两个数,并且发现单纯的按其中某一个数来排序的话都不行,那么很可能就是这种类型的题,需要两个数做某种运算后一块判断。判断出类型后,由于这道题的数据范围非常的大,还需要采用高精度算法来实现。

高精度算法

之前不管是高精度乘还是高精度除都是用数组来表示的,不过通过这道题发现用向量来做会更简单,这样就不需要字符数组与整型数组来回转换了。

高精乘

vector<int> mul(vector<int> a,int b)
{
    reverse(a.begin(),a.end());
    int t=0;
    vector<int>c;
    for(int i=0;i<a.size();i++)
    {
        t+=a[i]*b;
        c.push_back(t%10);
        t/=10;
    }
    while(t)
    {
        c.push_back(t%10);
        t/=10;
    }
    reverse(c.begin(),c.end());
    return c;
}

高精除

vector<int> division(vector<int> a,int b)
{
    vector<int>c;
    int flag=0;
    int t=0;
    for(int i=0;i<a.size();i++)
    {
        t=t*10+a[i];
        int x=t/b;
        if(x||flag)
        {
            flag=1;
            c.push_back(x);
        }
        t%=b;
    }
    return c;
}

贪心过程

struct s
{
    int l,r;
}x[10001];
bool cmp(s x,s y)
{
    return x.l*x.r<y.l*y.r;
}
vector<int> max_cmp(vector<int> a,vector<int> b)//找出获得最多金币的最小值
{
    if(a.size()!=b.size())
        return a.size()>b.size()?a:b;
    return a>b?a:b;
}
int main()
{
    int n;
    cin>>n;
    for(int a=0;a<=n;a++)
    {
        cin>>x[a].l>>x[a].r;
    }
    sort(x+1,x+n+1,cmp);
    vector<int>temp1,temp2;
    temp1.push_back(1);
    for(int a=1;a<=n;a++)
    {
        temp1=mul(temp1,x[a-1].l);
        temp2=max_cmp(temp2,division(temp1,x[a].r));
    }
    for(int a=0;a<temp2.size();a++)
        cout<<temp2[a];
    return 0;
}

3、约瑟夫环——递归的方法

对于约瑟夫环,本身不难,有很多种方式去做,但用递归可以说是独辟蹊径,很考验思维。对于递归的过程,有点模拟的意思。我们先把所有人围成一个环,第一个人出环后形成新的环,但是这个新的环却不是连续的,所以我们要对它进行重新编号。把出环的这个人的下一个人编为初始编号(这里认为初始编号为0),从新环去找要出环的人,那么找到了要出环的人之后怎么确定他的原始编号呢?——>(新环编号+m)%n=旧环编号。所以,递推公式就可以写出来了——>ysf(n,m,i)=(ysf(n-1,m,i-1)+m)%n。
我们可以模拟这个过程,首先一个环,有人出来之后就在这个环的外围再生成一个新环,以此类推,每个环都可以根据这个公式用新环给推出来。直到i=1的时候,递推结束,开始回溯,找到原始编号。

int ysf(int n,int m,int i)
{
    if(i==1)return (m-1)%n;
    return (ysf(n-1,m,i-1)+m)%n;
}

4、雷涛的小猫(贪心专题里做出来的dp题)

一点:根据数据范围,三重循环一定会tle,所以这时候就需要一个pre数组来保存当前高度所能得到的最大苹果数。

#include <bits/stdc++.h>

using namespace std;
int dp[7005][7005],x[7005][7005],pre[7005];
/*int solve(int x[],int n,int m)
{
    int ans=0;
    int sum=0;
    for(int a=0;a<n;a++)
    {
        sum+=x[a];
        if(sum>m){ans++;sum=0;sum+=x[a];}
    }
    if(sum!=0)ans++;
    return ans;
}*/
int main()
{
    int n,m,delta;
    cin>>n>>m>>delta;
    for(int a=1;a<=n;a++)
    {
        int t;
        cin>>t;
        int tt;
        while(t--)
        {
            cin>>tt;
            x[a][tt]++;
        }
    }
    int sum=0;
    for(int a=m;a>=0;a--)
    {
        for(int b=1;b<=n;b++)
        {
            dp[b][a]=x[b][a]+dp[b][a+1];
            dp[b][a]=max(dp[b][a],pre[a+delta]+x[b][a]);
            pre[a]=max(pre[a],dp[b][a]);
            sum=max(sum,dp[b][a]);
        }
    }
    cout<<sum<<endl;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值