2017暑假第二阶段第一场 总结

15 篇文章 0 订阅
7 篇文章 0 订阅

T1 数三角形

问题描述

给出一个正整数n,从1,2,3…..n 中选出三个不同整数,使得以它们为三边长可以组成三角形,问
总共有多少种不同的三角形?
例如,n=5 时有三种:(2,3,4) , (2,4,5) , (3,4,5)

输入格式

一个正整数n

输出格式

一个整数,表示三角形的个数


递推即可。

设f[i]表示n为i时的方案总数,那么f[i+1]相比f[i],多出的方案为最长边为i的三角形个数。分奇偶性不难推导。

#include<iostream>
using namespace std;
long long Ans,i,N;

int main()
{
    cin>>N;
    for(i=4;i<=N;i++)
    {
        if(i&1)Ans+=(i-3)*(i-1)/4;
        else Ans+=(i-2)*(i-2)/4;
    }
    cout<<Ans;
}

T2 翻硬币

问题描述

两个玩家在玩一个有趣的翻硬币游戏。

有 N 枚硬币排成一排,有的正面朝上,有的反面朝上。从左往右硬币按1 到N 编号。玩家轮流操作。每次操作,玩家选一枚正面朝上的硬币,将它翻转,同时在该硬币左侧连续四个硬币中,再任选一个硬币,将其翻转。
具体而言,假设第i号硬币正面朝上。若将第i号硬币翻转后,必须在编号为i-1,i-2,i-3,i-4的四个硬币中选一个进行翻转。若i<=4,则可只翻转i号硬币,也可以再在1到i-1之间选一个进行翻转。

谁没有硬币可翻谁就算输。两个玩家都非常聪明,问先手是否获胜?

输入格式

第一行,一个正整数T,表示接下来有T组测试数据。对于每组测试数据:

第1行,一个整数N,表示硬币的数量。
第2行,N个空格间隔的整数(0和1),从左往右依次表示游戏开始前硬币的情况,其中数字0表示正面朝下,数字1表示正面朝上。

输出格式

T行,每行对应一组测试数据的答案。若先手胜输出”Yes” 否则输出“No”


将0,1,01,001,0001,……的SG值计算出后,显然可以用其中某些值的异或和表示任意状态。
容易发现规律,在上面的数列中,当1为从左往右第i个数时,SG值为i%5。

#include<stdio.h>
int T,N;

int main()
{
    int i,x,SG;

    scanf("%d",&T);

    while(T--)
    {
        SG=0;
        scanf("%d",&N);
        for(i=1;i<=N;i++)
        {
            scanf("%d",&x);
            if(x)SG^=i%5;
        }
        if(SG)puts("Yes");
        else puts("No");
    }
}

T3 小鸟

问题描述

有一排n棵树,第i棵树的高度是Di。 一群小鸟要从第1棵树飞到第n棵树去玩。
不同小鸟的飞跃能力不同,第i只小鸟的飞跃能力为ki,表示如果当前它位于第x号树,那么它可以飞到x+1,x+2,……,x+ki号树上去,也就是一次可以飞过ki棵树。
如果小鸟飞到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。 小鸟们希望最小化劳累值,请你计算每只小鸟达到终点所需最小劳累值。

输入格式

第一行,一个整数N(2<=N<=100000)
第二行,N个空格间隔的整数,第i个数表示第i棵树的高度Di。(1<=Di<=10^9)
第三行,一个整数Q(1<=Q<=25),表示小鸟的数量
接下来Q行,每行一个整数,其中第i个整数表示第i只小鸟的飞跃能力ki。

输出格式

Q行,每行一个整数,表示对应小鸟的劳累值


单调队列优化DP。

设f[i]表示小鸟飞到第i棵树时的最小劳累值,很容易写出状态转移方程。时间复杂度为O(Nk)。可以用单调队列优化为O(N)的级别。

根据题目要求,维护一个单调递增的队列。当f的值相等时,D较大的更优。

#include<stdio.h>
#include<deque>
#define MAXN 100005
using namespace std;
int D[MAXN],N,Q,K,f[MAXN];

int main()
{
    int i,k;
    deque<int>S;

    scanf("%d",&N);
    for(i=1;i<=N;i++)scanf("%d",&D[i]);

    scanf("%d",&Q);
    for(k=1;k<=Q;k++)
    {
        f[1]=0;
        while(S.size())S.pop_back();
        S.push_back(1);

        scanf("%d",&K);

        for(i=2;i<=N;i++)
        {
            if(S.front()<i-K)S.pop_front();
            f[i]=f[S.front()];
            f[i]+=D[i]>=D[S.front()];
            while(S.size()&&(f[S.back()]>f[i]||(f[S.back()]==f[i]&&D[S.back()]<=D[i])))S.pop_back();
            S.push_back(i);
        }
        printf("%d\n",f[N]);
    }
}

T4 乘车路线

问题描述

编号为 1.. N的N座城镇用若干仅供单向行驶的道路相连,每条道路上均有两个参数:道路长度(length)和在该条道路上行驶的费用(cost)。
BOB准备从城镇 1 出发到达城镇 N,但他目前只有 W 块钱,为此,你需要帮助他寻找一条从城镇1到城镇 N 在他能支付的前提下的一条最短路线。

输入格式

第一行为钱的数目W (0<=w<=1000)
第二行为城镇数目N(2<=N<=100)
第三行为为道路条数K(1<=K<=10000)
随后的 K 行每行为一条道路的信息,包含 4个数值(S,D,L,T)其中S为道路的起点, D为道路的终点 , L为道路长度, T为所需支付费用。 (1<=S,D<=N,1<=L<=100,0<=T<=100)

输出格式

输出最短长度,若无解,则输出“NO”


乍一眼可能觉得是网络流,但是并不是。

方法一:
由于数据规模不大,记忆化搜索加上其他一些剪枝可以跑得飞快,甚至优于方法二。
m[i][j]表示走到i号点,费用为i的最小时间。

#include<stdio.h>
#include<cstring>
const int MAXN=105,MAXM=10005,inf=1e9;
int W,N,K,Ans=inf;
int m[105][1005];

int tot,en[MAXM],las[MAXN],nex[MAXM],len[MAXM],cos[MAXM];
void ADD(int x,int y,int l,int t)
{
    en[++tot]=y;
    nex[tot]=las[x];
    las[x]=tot;
    len[tot]=l;
    cos[tot]=t;
}

void DFS(int x,int t,int c)
{
    int i,y;
    if(t>Ans)return;//最优化剪枝
    if(c>W)return;//可行性剪枝
    if(m[x][c]<t)return;//记忆化
    m[x][c]=t;

    if(x==N)
    {
        if(Ans>t)Ans=t;
        return;
    }
    for(i=las[x];i;i=nex[i])y=en[i],DFS(y,t+len[i],c+cos[i]);
}

int main()
{
    int i,x,y,l,t;

    memset(m,60,sizeof(m));
    m[1][0]=0;

    scanf("%d%d%d",&W,&N,&K);
    for(i=1;i<=K;i++)scanf("%d%d%d%d",&x,&y,&l,&t),ADD(x,y,l,t);

    DFS(1,0,0);
    if(Ans==1e9)puts("NO");
    else printf("%d",Ans);
}

方法二
设dis[i][j]表示走到i号点,且花费为j时的最短距离。那么对SPFA稍加改动即可。

#include<stdio.h>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN=105,MAXM=10005;
int Ans=1e9;
int W,N,K;

int tot,en[MAXM],las[MAXN],nex[MAXM],len[MAXM],cos[MAXM];
void ADD(int x,int y,int l,int t)
{
    en[++tot]=y;
    nex[tot]=las[x];
    las[x]=tot;
    len[tot]=l;
    cos[tot]=t;
}

bool mark[MAXN];
int dis[105][1005];

void SPFA()
{
    queue<int>Q;
    memset(dis,60,sizeof(dis));

    int i,j,x,y;
    for(i=0;i<=W;i++)dis[1][i]=0;
    Q.push(1);

    while(Q.size())
    {
        x=Q.front();Q.pop();mark[x]=false;
        for(i=las[x];i;i=nex[i])
        {
            y=en[i];
            for(j=cos[i];j<=W;j++)
            {
                if(dis[y][j]>dis[x][j-cos[i]]+len[i])
                {
                    dis[y][j]=dis[x][j-cos[i]]+len[i];
                    if(!mark[y])mark[y]=true,Q.push(y);
                }
            }
        }
    }
}

int main()
{
    int i,x,y,l,t;

    scanf("%d%d%d",&W,&N,&K);
    for(i=1;i<=K;i++)
    {
        scanf("%d%d%d%d",&x,&y,&l,&t);
        ADD(x,y,l,t);
    }
    SPFA();
    for(i=0;i<=W;i++)Ans=Ans>dis[N][i]?dis[N][i]:Ans;

    if(Ans>=dis[0][0])puts("NO");
    else printf("%d",Ans);
}

总结

本次比赛题目总体难度不难。T1静下心来很容易就能AC;T2主要考查博弈的结论;T3是数据结构单调队列比较裸的题;T4由于数据范围不大,应该想到可以采用比较暴力的做法。

优点主要是T1、T2做得较快;T3、T4虽然没能AC,但第一感觉就是正解。
缺点主要暴露在:对学过的某些数据结构不够熟悉,否则能够AC掉T3,也不会浪费掉太多时间;不敢在T4的位置写搜索,在实在没想到更优的解法时也应该尽量把优化后的搜索写下来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值