您的位置:首页 > 其它

noip2013普及组解题报告

2013-12-29 12:36 316 查看


第一题:记数问题


题目描述 Description

试计算在区间1到n的所有整数中,数字x(0≤x≤9)共出现了多少次?例如,在1到11中,即在1、2、3、4、5、6、7、8、9、10、11中,数字1出现了4次。


输入描述 Input Description

输入共1行,包含2个整数n、x,之间用一个空格隔开。


输出描述 Output Description

输出共1行,包含一个整数,表示x出现的次数。


样例输入 Sample Input

11 1


样例输出 Sample Output

4

这题与往年的题目有点相似,数据范围也才1——1000000,不大,所以可以直接做:

#include <iostream>
using namespace std;
int x,r,l,k,ans;
int main(){
cin>>r>>x;
for(l=1;l<=r;l++)
{
k=l;
while(k!=0){if(k%10==x)ans++;k/=10;}
}
cout<<ans;
return 0;
}



第二题:表达式求值


题目描述 Description

给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。


输入描述 Input Description

输入仅有一行,为需要你计算的表达式,表达式中只包含数字、加法运算符“+”和乘法运算符“*”,且没有括号,所有参与运算的数字均为0到2^31-1之间的整数。输入数据保证这一行只有0~9、+、*这12种字符。


输出描述 Output Description

输出只有一行,包含一个整数,表示这个表达式的值。注意:当答案长度多于4位时,请只输出最后4位,前导0不输出。


样例输入 Sample Input

[Sample 1]

1+1*3+4

[Sample 2]

1+1234567890*1

[Sample 3]

1+1000000003*1


样例输出 Sample Output

[Sample 1]

8

[Sample 2]

7891

[Sample 3]

4

其实题目本意非常简单,就是给个表达式,我的思路是:

1)读入一个数字读入一个符号(因为题目让我们只保留后四位,所以读入数字时,要mod 10000)

2)根据优先级,先处理乘号

3)直接累加

code:

#include<iostream>
using namespace std;
unsigned long long num[100000];
char sign[100000];
unsigned long long i,l=1,ans;
int main(){
cin>>num[1];
while(cin>>sign[l]){
l++;
cin>>num[l];
num[l]%=10000;
}
l--;
for(i=1;i<=l;i++)
if(sign[i]=='*')
{num[i+1]=num[i]*num[i+1];num[i]=0;num[i+1]%=10000;}
for(i=1;i<=l+1;i++)ans+=num[i];
cout<<ans%10000;
return 0;
}



第三题:小朋友的数字


题目描述 Description

有n个小朋友排成一列。每个小朋友手上都有一个数字,这个数字可正可负。规定每个小朋友的特征值等于排在他前面(包括他本人)的小朋友中连续若干个(最少有一个)小朋友手上的数字之和的最大值。

作为这些小朋友的老师,你需要给每个小朋友一个分数,分数是这样规定的:第一个小朋友的分数是他的特征值,其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值。

请计算所有小朋友分数的最大值,输出时保持最大值的符号,将其绝对值对p取模后输出。


输入描述 Input Description

第一行包含两个正整数n、p,之间用一个空格隔开。

第二行包含n个数,每两个整数之间用一个空格隔开,表示每个小朋友手上的数字。


输出描述 Output Description

输出只有一行,包含一个整数,表示最大分数对p取模的结果。


样例输入 Sample Input

[Sample 1]

5 997

1 2 3 4 5

[Sample 2]

5 7

-1 -1 -1 -1 -1


样例输出 Sample Output

[Sample 1]

21

[Sample 2]

-1


数据范围及提示 Data Size & Hint

【样例说明】

样例1小朋友的特征值分别为1、3、6、10、15,分数分别为1、2、5、11、21,最大值21对997的模是21。

样例2小朋友的特征值分别为-1、-1、-1、-1、-1,分数分别为-1、-2、-2、-2、-2,最大值-1对7的模为-1,输出-1。

【数据范围】

对于50%的数据,1≤n≤1,000,1≤p≤1,000所有数字的绝对值不超过1000;

对于100%的数据,1≤n≤1,000,000,1≤p≤10^9,其他数字的绝对值均不超过10^9。

比较easy的一个DP,特殊值就用最大子段和来求,分数不能直接求,要找规律,要么越来越大,要么越来越小,越来越小输出第一个,越来越大输出第n个

时间复杂度O(2n)

code:

#include<cstdio>

#include<cstring>

#include<cstdlib>

#define LL long long// 因为这道题有可能会很大的数,用longlong

using namespace std;

LL maxx ( LL x, LL y){ return x > y ? x : y; } // 求两个数的最大值

LL n, p, a[1100000];// a是一开始的那一段数

LL b[1100000],c[1100000]; // b是特征值,c是分数

int main (){

int i, j;

scanf ( "%lld%lld", &n,&p );

for ( i = 1; i <= n; i ++ )

scanf( "%lld", &a[i] );

b[1] = a[1];

LL now = maxx ( a[1], 0 ); // 看看第一个数是正数还是负数,如果是负数,则忽略这一个数,免得使统计的数变小

for ( i = 2; i <= n; i ++ ){

b[i]= b[i - 1]; // 先等于前面的特征值

now+= a[i]; // 把这个累加值加上当前小孩子手上的数字

b[i] = maxx ( now, b[i] ); // 看看是前面特征值的大还是累加值大

if( now < 0 ) now = 0; // 如果累加值比0小,那又把它变成0,重新累加

}

c[1] = b[1];

c[2] = c[1] + b[1]; // c1和c2是固定的,可以现在前面算

int ok = 0; // ok就是看是最后的值更大些还是第一个更大些

LL tmp = c[1];

for ( i = 2; i < n; i ++ ){

tmp += maxx ( b[i], 0ll );

if ( tmp >= 0 ){ // 如果这个累加值比0大,那么就证明会累加越来越大

ok = 1;

break;

}

}

for ( i = 3; i <= n; i ++ )

c[i]= ( c[i - 1] + maxx ( b[i - 1], 0ll ) ) % p; // 求每个人分数的最大值

if ( ok ) // 如果最后的大些

printf ( "%d\n", int ( c
) ); // 输出最后的

else // 如果第一个大些

printf ( "%d\n", int ( c[1] % p ) );// 输出第一个

return 0;

}



第四题:车站分级


题目描述 Description

一条单向的铁路线上,依次有编号为1, 2, …, n的n个火车站。每个火车站都有一个级别,最低为1级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站x,则始发站、终点站之间所有级别大于等于火车站x的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

例如,下表是5趟车次的运行情况。其中,前4趟车次均满足要求,而第5趟车次由于停靠了3号火车站(2级)却未停靠途经的6号火车站(亦为2级)而不满足要求。

现有m趟车次的运行情况(全部满足要求),试推算这n个火车站至少分为几个不同的级别。


输入描述 Input Description

第一行包含2个正整数n, m,用一个空格隔开。

第i+1行(1≤i≤m)中,首先是一个正整数s_i(2≤s_i≤n),表示第i趟车次有s_i个停靠站;接下来有s_i个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。


输出描述 Output Description

输出只有一行,包含一个正整数,即n个火车站最少划分的级别数。


样例输入 Sample Input

[Sample 1]

9 2

4 1 3 5 6

3 3 5 6

[Sample 2]

9 3

4 1 3 5 6

3 3 5 6

3 1 5 9


样例输出 Sample Output

[Sample 1]

2

[Sample 2]

3

拓扑排序模板,只要在拓扑排序的过程中找到不能删除的,需要的层次就++

相比较往年是为数不多的100行以内的题目了

#include<stdio.h>
#include<iostream>
#include<vector>
#include<memory.h>
using namespace std;
const int MAX_N = 2002;
int N,M;int P;
int s[MAX_N]; //how many stations in route i
int level[MAX_N]; //level
int infor[MAX_N][MAX_N]; //info about route i
vector<int> d[MAX_N]; //don't stop here
vector<int> p[MAX_N]; //stop here
int adj[MAX_N][MAX_N];
int con[MAX_N];
void init()
{
int i,j,k;
scanf("%d %d",&N,&M);
memset(adj,-1,sizeof(adj));
P=N;
for (i=1;i<=M;i++)
{
scanf("%d",&s[i]); //读入
for (j=1;j<=s[i];j++)
scanf("%d",&infor[i][j]);
if (infor[i][s[i]]-infor[i][1]+1==s[i]) continue;
//如果在这条线路当中最后一个车站-第一个车站序号+1=总的经过车站数量,也就没有不停的车站,就不存在做的可能
//而题目保证一条线路中至少有2个停的车站,所以放心
for (j=infor[i][1],k=1;j<=infor[i][s[i]];j++)
{
if (j==infor[i][k]) {p[i].push_back(j);k++;}
else {d[i].push_back(j);} //扫描那些节点在d停车的节点中 那些在p不停的当中
}
P++; //产生bt节点
for (j=0;j<d[i].size();j++)
adj[d[i][j]][P]=0,con[P]++; //构造(Dij,P)有向边 con[P]入度++
for (j=0;j<p[i].size();j++)
adj[P][p[i][j]]=1,con[p[i][j]]++;
}
for (i=1;i<=P;i++)
level[i]=1;//初始化 level至少为1
}
int find_zero()
{
int i;
for (i=1;i<=P;i++)
if (con[i]==0) return i; //寻找入度为0的节点
return -1;
}
void work()
{
int i,j,k;
for (i=1;i<=P;i++)
{
k=find_zero();
if (k==-1) return ; //如果没有入度为0的顶点 说明做完了
for (k<=N?j=N+1:j=1;k<=N?j<=P:j<=N;j++)
//完全可以写for (j=1;j<=P;j++)
//不过这是常数级别优化
//因为根据题意可知
//如果存在边 (x,y) 则x∈车站T集合 y∈bt节点集合
//否则 y∈车站T集合 x∈bt节点集合
if (adj[k][j]!=-1) //-1表示没有变
{
level[j]=max(level[j],level[k]+adj[k][j]); //求最长路
adj[k][j]=false; //删除这条边
con[j]--;  //入度--
}
con[k]=-1; //标记已经做过这个顶点
}
}
void put()
{
int i;
int ans = 0;
for (i=1;i<=P;i++)
ans  = max(ans,level[i]); //寻找最大等级
printf("%d",ans);
}
int main()
{
init();
work();
put();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: