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