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

来源:互联网 发布:wear软件安装不了 编辑:程序博客网 时间:2024/05/19 20:42

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 100005using 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的位置写搜索,在实在没想到更优的解法时也应该尽量把优化后的搜索写下来。

原创粉丝点击