【Test 2016-10-1】模拟+差分序列+spfa最短路+贪心

国庆day1分班水题大作战0v0

(一)minimum

这里写图片描述


【题解】由a推b显然情况太多了,不如反过来b推a,模拟一下能除就除,不然就减。记得特判1和0。


#include <cstdio>
#define LL long long
LL a,b,k,ans;
    void search(LL x,LL y)
    {
        for (;x!=y;)
        {
            LL p=y-y/k*k;
            if (!p) 
            {
                if (y/k>=x) y/=k,++ans;
                else ans+=(y-x),y-=(y-x);
            }
            else 
            {
                if (y-p>=x) y-=p,ans+=p;
                else ans+=(y-x),y-=(y-x);
            }
        }
    }
int main()
{
        scanf("%lld%lld%lld\n",&a,&b,&k);
        if (k<=1) ans=b-a;
        else search(a,b);
        printf("%lld\n",ans);
    return 0;
}

(二)plusplus

这里写图片描述


【题解】(乱搞剪枝可以过100嘿嘿嘿)
先把原序列转换成差分序列,这样对于每个操作只需要改变两个数。


#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
long long a[1000005];
int i,n,m,now=0,l,r,d,ans=-1;
int main()
{
    scanf("%d%d\n",&n,&m);
    for (i=1;i<=n;++i) scanf("%lld\n",&a[i]);scanf("\n");
    for (i=n;i;--i) a[i]-=a[i-1];
    for (i=1;i<=n;++i) if (a[i]==0) now++;
    if (now==n) ans=0;
    for (i=1;i<=m && ans==-1;++i)
    {
        scanf("%d%d%d\n",&l,&r,&d);++r;
        if (a[l]==0 && a[l]-d!=0) --now;
        if (a[l]!=0 && a[l]-d==0) ++now;
        if (r<=n)
        {
            if (a[r]==0 && a[r]+d!=0) --now;
            if (a[r]!=0 && a[r]+d==0) ++now;
        }
        a[l]=a[l]-d;a[r]=a[r]+d;
        if (now==n) ans=i;
    }
    printf("%d\n",ans);
    return 0;
}

(三)maze

  • 【题目描述】
    小 T 被放到了一个迷宫之中, 这个迷宫由 n 个节点构成, 两个节点之间可能存在多条无向边,小 T 的起点为 1 号节点,终点为 n 号节点。有 m 条无向边,对于每一条无向边,存在一个喋血值(∈N*,且≤100) ,即走过这条边的花费。另外,还有 k 个节点上有治疗药,即若小 T 走到这个节点上时(不妨称这个点为治愈点),他身上所累积的喋血值会归零。小 T希望以最小的喋血值走完迷宫。
  • 【Sample Input】
    (第 1 行三个整数 n,m,k 分别表示有 n 个节点,m 条无向边,以及 k 个治愈点。
    第 2 行到 m+1 行,每一行有三个整数 x,y,z 表示 x 到 y 有一条喋血值为 z 的无向边。
    第 n+2 行有 k 个整数,分别为治愈点的结点编号。
    PS:保证数据中没有负权回路。保证治愈点不重复。)
    3 3 1
    1 2 100
    2 3 1
    1 3 3
    2
  • 【Sample Output】(一行一个数表示小 T 走完迷宫的最小喋血值。无法走出迷宫,输出“Oh no!”。)
    1

【题解】从终点跑一遍spfa,然后找到起点能到达的治愈点到终点的最小距离即为答案。起点到不了终点输出oh no。


#include <cstdio>
#include <cstring>
#include <iostream>
#define inf 1000000000
struct edge{ int to,s,nxt;}e[100000];
int n,m,k,ans,cnt,dis[5005],dis1[5005],disn[5005],q[250005],fi[5005];
bool bo[5005];
    void add(int u,int v,int w)
    {
        e[++cnt].to=v;e[cnt].s=w;
        e[cnt].nxt=fi[u];fi[u]=cnt;
    }
    void spfa(int S)
    {
        for (int i=1;i<=n;++i) 
            if (i!=S) dis[i]=inf;
        for (int i=1;i<=n;++i) bo[i]=false;
        dis[S]=0;
        int h=1,t=1;
        for (bo[q[1]=S]=true;h<=t;bo[q[h++]]=false)
            for (int i=fi[q[h]];i;i=e[i].nxt)
            {
                int v=e[i].to;
                if (dis[v]<=dis[q[h]]+e[i].s) continue;
                dis[v]=dis[q[h]]+e[i].s;
                if (!bo[v]) bo[q[++t]=v]=true;
            }
    }
int main()
{
        scanf("%d%d%d\n",&n,&m,&k);
        for (int i=1;i<=m;++i)
        {
            int u,v,w;
            scanf("%d%d%d\n",&u,&v,&w);
            add(u,v,w);add(v,u,w);
        }
        spfa(1);for (int i=1;i<=n;++i) dis1[i]=dis[i];
        spfa(n);for (int i=1;i<=n;++i) disn[i]=dis[i];
        ans=dis1[n];
        for (int i=1;i<=k;++i)
        {
            int x;scanf("%d\n",&x);
            if (dis1[x]!=inf) ans=std::min(ans,disn[x]);
        }
        if (ans==inf) printf("Oh no!");
        else printf("%d\n",ans);
    return 0;
}

(四)queue

  • 【题目描述】
    实话实说,给OIER大神们排队这种工作是最让人头疼的事情了。因为同学们都有自尊心,都不愿意排后面。
    现在共有 n 个同学要排成一列,每个同学有两个属性:影响力和承受能力。给一个同学造成的心理创伤指数等于所有在他前面同学的影响力之和减去他的承受能力。
    请你帮忙安排一下点名顺序,尽量使受到心理创伤最大的同学少受创伤。

  • 【SampleInput】(第1行是整数n,表示同学的个数。第2~n+1行每行两个自然数,同学的影响力和承受能力。)
    3
    10 3
    2 5
    3 3

  • 【Sample Output】(输出 1 行 1 个整数,为你安排的顺序中受到心理创伤最大的同学受到的创伤。)
    2

【题解】经典贪心问题加法版。
首先,对于任意两个人 i,i+1 他们的顺序对其他人无影响。
贪心考虑对 i,i+1,一定满足:a[i]+b[i] < a[i+1]+b[i+1]。
排个序过了。(记得答案可能为负数


#include <cstdio>
#include <algorithm>
#define LL long long
#define inf 2147483647
int n,ans,a[50005],b[50005],k[50005];
    bool comp(int x,int y)
    {
        return a[x]+b[x]<a[y]+b[y];
    }
int main()
{
        scanf("%d\n",&n);
        for (int i=1;i<=n;++i) scanf("%d%d\n",&a[i],&b[i]);
        for (int i=1;i<=n;++i) k[i]=i;
        std::sort(k+1,k+n+1,comp);
        ans=-inf;int p=0;
        for (int i=1;i<=n;++i)
        {
            ans=std::max(ans,p-b[k[i]]);
            p+=a[k[i]];
        }
        printf("%d\n",ans);
    return 0;
}

【题外话】本日水题打的有点爆炸,80+100+100+60=340。感觉自己还是太菜了。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值