大二个人训练赛DP图论组 #2

http://acm.nefu.edu.cn/JudgeOnline/index.php

A.预测商品

你可以知道一个物品每天的价格波动,这样你就可以在价低时买,在价格高时卖。但是你每天只能交易一个物品,就是说你只能买或者卖或者什么都不做。一共有n天,到第n天结束后,你最多得可以多少钱。

同CF865D,http://codeforces.com/contest/865/problem/D

优先队列

物品买卖的题,碰见一个东西,啥都不说,先预定(预定,但不买)下来(放入优先队列),如果它以后能升值,则刷新这个股票的价值,然后获利,如果不能升值,则最后的时候不计算它;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define inf 0x3f3f3f
using namespace std;
priority_queue <int,vector<int>,greater<int> > q;
int n,m;
int main()
{
    cin>>n;
    long long ans=0;
    for(int i=0;i<n;i++)
    {
        cin>>m;
        q.push(m);
        if (q.top()<m)
        {
            ans+=m-q.top();
            q.pop();
            q.push(m);
        }
    }
    printf ("%I64d\n",ans);
    return 0;
}

B.传说之剑

一名传奇巫师接受了一项任务,要为一把传说之剑附魔。巫师一开始有n点魔法值,他能够吟唱两种附魔咒语。 第一种咒语消耗s1点魔法值,为传说之剑增加v1点攻击力。第二种咒语消耗s2点魔法值,为传说之剑增加v2点攻击力。 只要巫师还有足够的魔法值,他可以重复吟唱任一咒语。 求传说之剑最多有多少附魔攻击力。(所有数据<=2000000000)

如果数据不这么大,就是一道背包裸题。。。

思路:直接暴力枚举会TLE,先计算s1 s2的LCM,分两种情况讨论。

1.n<lcm 直接枚举

2.n>=lcm 先求r=n%lcm,将n分为两部分,n-r和r,第一部分直接计算,第二部分暴力枚举。

注意:枚举时使用s1 s2中较大的进行枚举!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f
using namespace std;
typedef long long ll;
ll t,n,s1,v1,s2,v2;
ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld%lld%lld%lld%lld",&n,&s1,&v1,&s2,&v2);
        if (s1<s2)
        {
            swap(s1,s2);
            swap(v1,v2);
        }
        ll ans=0;
        ll lcm=s1/gcd(s1,s2)*s2;
        ll mod=n%lcm;
        if (n<lcm)
        {
            ll num=0;
            while(num*s1<=n)
            {
                ans=max(ans,(n-num*s1)/s2*v2+num*v1);
                num++;
            }
        }
        else
        {
            ll tmp=(n-mod)/lcm*max(lcm/s1*v1,lcm/s2*v2);
            ll num=0;
            ll res=mod;
            while(num*s1<=res)
            {
                 ans=max(ans,(res-num*s1)/s2*v2+num*v1);
                num++;
            }
            ans+=tmp;
        }
        printf ("%lld\n",ans);
    }
    return 0;
}

 

C.魔法王国

在遥远的青青草原上,有一座魔法王国,王国里有n个城市,现在魔法王国的国王想要在城市间修一些道路,让这些城市互相连通(任意两个城市间均是可达的),在魔法王国修路当然需要魔法了,现在知道有m条双向道路可以被修建,但是每一条道路都需要一定的魔法值,只有魔法值不小于这个值的魔法师才能修建,而且修建之后这个魔法师的魔法值还会损失一。现在魔法王国的国王想要知道如果他只找一个魔法师来建造道路,那么这个魔法师的魔法值至少为多少呢?(题目数据保证一定可以将所有的城市联通)

最小生成树

修每次路的时候,魔法值会减1,除此之外,就是一道最小生成树裸题嘛,每次判断一下就可以了

#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set>
using namespace std;
#define ll long long
const int inf=0x3f3f3f3f;
struct data
{
    int a,b,v;
}w[100001];
bool cmp(data q,data e)
{
    return q.v<e.v;
}
bool kkk(int q,int e)
{
    return q>e;
}
int pre[10001];
int n,m,t;
int find(int x)
{
    if (pre[x]!=x)
    {
        pre[x]=find(pre[x]);
    }
    return pre[x];
}
void join(int x,int y)
{
    int dx=find(x);
    int dy=find(y);
    if (dx!=dy)
    {
        pre[dx]=dy;
    }
}
int kk[100001];
int main()
{
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&m);
        for (int i=0;i<=n;i++) pre[i]=i;
        for (int i=0;i<m;i++)
        {
            scanf("%d%d%d",&w[i].a,&w[i].b,&w[i].v);
        }
        sort(w,w+m,cmp);
        int k=0;
        for (int i=0;i<m;i++)
        {
            if (find(w[i].a)!=find(w[i].b))
            {
                kk[k++]=w[i].v;
                join(w[i].a,w[i].b);
            }

        }
        sort(kk,kk+k,kkk);
        int kkkk=kk[0]-1;
        int ans=kk[0];
        for(int i=1;i<k;i++)
        {
            if (kkkk<kk[i])
            {
                ans+=kk[i]-kkkk;
                kkkk=kk[i];
            }
            kkkk--;
        }
        printf ("%d\n",ans);

    }
    return 0;
}

D.东南西北

历史上有八卦阵之说,进入到人很难正确找到出口,有八个门。有个大师想创造一个简单版本的,却遇上一个难题:有东,南,西,北四个门位置,假设目前正在东门,大师想知道走n步回到东门的走法共有多少种。每次处于一个门时,走一步只能到其他三个门,且不能不走,每两个门之间都是互通的。请输出走n步之后回到原地的走法数量,走法数量可能非常大,输出对1000000007 (1e9+7)取余。比如在东门,走2步回到东门有多少种走法:东->北->东,东->南->东,东->西->东共三种走法。

简单dp

画一个简单的图会发现是一个完全三叉树,不用预处理可以过,不超时

二维dp,一维代表步数,二维代表在不在起点

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll dp[10000001][2];
int n,m,t;
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        dp[2][0]=3;
        dp[2][1]=6;
        for(int i=3; i<=n; i++)
        {
            dp[i][1]=(dp[i-1][1]*2+dp[i-1][0]*3)%mod;//现在一步不在起点可以由前一步不在起点*2和前一步在起点*3得到
            dp[i][0]=(dp[i-1][1])%mod;//现在一步在起点由前一步不在起点得到
        }
        printf ("%lld\n",dp[n][0]);
    }
    return 0;
}

A题比赛时就在想用dp写,然后学长说数据有问题,就没再看

B题真的当时没思路,数据太大了,看题解之前我也没想到是用lcm做的。。

C题不说了,

D题竟然因为不是多组输入wa了两次。。。。。。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值