2012年NOIP普及组复赛真题解析

2012年NOIP普及组T1-质因数分解

题目描述

已知正整数n是两个不同的质数的乘积,试求出两者中较大的那个质数。

输入格式

输入只有一行,包含一个正整数n。

输出格式

输出只有一行,包含一个正整数p,即较大的那个质数。

输入输出样例

输入样例1:
21
输出样例1:
7

说明

【数据范围】

n<=2*10^9

耗时限制1000ms   内存限制128MB

解析:

考点:循环结构

思路:

唯一分解定理:任何一个大于 1 的数字都可以唯一的分解为若干个质数的成绩。

如题,如果 n 可以表示为两个质数的乘积,那么这两个质数也就一定是除了 1 和n 外,唯二的因数了。我们只需循环从 2 开始,找到第一个质因数 i,然后答案就是n/i 了。

参考代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+5;
int n;

int main(){
    cin >> n;
    for(int i = 2; i*i<=n; i++) {
        if(n%i==0) {
            cout << n/i;
            break;
        } 
    }
    return 0;
}

2012年NOIP普及组T2-寻宝

题目描述

传说很遥远的藏宝楼顶层藏着诱人的宝藏。小明历尽千辛万苦终于找到传说中的这个藏宝楼,藏宝楼的门口竖着一个木板,上面写有几个大字:寻宝说明书。说明书的内容如下:藏宝楼共有N+1层,最上面一层是顶层,顶层有一个房间里面藏着宝藏。除了顶层外,藏宝楼另有N层,每层M个房间,这M个房间围成一圈并按逆时针方向依次编号为0,…,M-1。其中一些房间有通往上一层的楼梯,每层楼的楼梯设计可能不同。每个房间里有一个指示牌,指示牌上有一个数字x,表示从这个房间开始按逆时针方向选择第x个有楼梯的房间(假定该房间的编号为k),从该房间上楼,上楼后到达上一层的k号房间。比如当前房间的指示牌上写着2,则按逆时针方向开始尝试,找到第2个有楼梯的房间,从该房间上楼。如果当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。

寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥”。

请帮助小明算出这个打开宝箱的密钥。

输入格式

第一行2个整数N和M,之间用一个空格隔开。N表示除了顶层外藏宝楼共N层楼,M表示除顶层外每层楼有M个房间。

接下来N*M行,每行两个整数,之间用一个空格隔开,每行描述一个房间内的情况,其中第(i-1)*M+j行表示第i层j-1号房间的情况(i=1,2,…, N;j=1,2,…,M)。第一个整数表示该房间是否有楼梯通往上一层(0表示没有,1表示有),第二个整数表示指示牌上的数字。注意,从j号房间的楼梯爬到上一层到达的房间一定也是j号房间。

最后一行,一个整数,表示小明从藏宝楼底层的几号房间进入开始寻宝(注:房间编号从0开始)

输出格式

输出只有一行,一个整数,表示打开宝箱的密钥,这个数可能会很大,请输出对20123取模的结果即可

输入输出样例

输入样例1:
2 3
1 2
0 3
1 4
0 1
1 5
1 2
1
输出样例1:
5

说明

【数据范围】

对于50%数据,有0<N≤1000,0<x≤10000;

对于100%数据,有0<N≤10000,0<M≤100,0<x≤1,000,000。

耗时限制1000ms   内存限制128MB

考点:模拟,概率论

参考代码



#include<bits/stdc++.h>
using namespace std;
const int MAXN=10100;
const int MAXM=110;
const int mod=20123;
bool dr[MAXN][MAXM];
int nxt[MAXN][MAXM];
int cnt[MAXN];
int main() {
    int n,m;
    cin>>n>>m;
    for(int i=1; i<=n; i++) {
        for(int j=0; j<m; j++) {
            cin>>dr[i][j]>>nxt[i][j];
        }
    }
    for(int i=1; i<=n; i++) {
        for(int j=0; j<m; j++) {
            if(dr[i][j]) {
                cnt[i]++;
            }
        }
    }
    int fang;
    cin>>fang;
    int ans=0;
    for(int ceng=1; ceng<=n; ceng++) {/*慢慢绕*/
        ans=(ans%mod+nxt[ceng][fang]%mod)%mod;/*同余定理*/
        int x=nxt[ceng][fang]%cnt[ceng];
        if(!x) {
            x=cnt[ceng];
        }
        while(x) {
            if(dr[ceng][fang]) {
                x--;
            }
            if(x) {
                fang++;
                fang%=m;
            }
        }
    }
    cout<<ans%mod;
    return 0;
}

2012年NOIP普及组T3-摆花

题目描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号。为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。

试编程计算,一共有多少种不同的摆花方案

输入格式

第一行包含两个正整数n和m,中间用一个空格隔开。

第二行有n个整数,每两个整数之间用一个空格隔开,依次表示a1、a2、……an。

输出格式

输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对1000007取模的结果

输入输出样例

输入样例1:
2 4
3 2
输出样例1:
2

说明

【数据范围】

对于20%数据,有0<n≤8,0<m≤8,0≤ai≤8;

对于50%数据,有0<n≤20,0<m≤20,0≤ai≤20;

对于100%数据,有0<n≤100,0<m≤100,0≤ai≤100。

耗时限制1000ms   内存限制128MB

解析:

考点:动态规划,记忆化搜索

一般搜索会超时

1.记忆化搜索

#include<bits/stdc++.h>
using namespace std;
const int maxn=105, mod = 1000007;
int n, m, a[maxn], rmb[maxn][maxn];
int dfs(int x,int k){
    if(k > m) return 0;
    if(k == m) return 1;
    if(x == n+1) return 0;
    if(rmb[x][k]) return rmb[x][k]; //搜过了就返回
    int ans = 0;
    for(int i=0; i<=a[x]; i++) ans = (ans + dfs(x+1, k+i))%mod;
    rmb[x][k] = ans; //记录当前状态的结果
    return ans;
}
int main(){
    cin>>n>>m;
    for(int i=1; i<=n; i++) cin>>a[i];
    cout<<dfs(1,0)<<endl;
    return 0;
}

2.动态规划

一、状态转移方程
状态定义:记 f[i][j] 表示摆 i 种花,共摆 j 盆的方案数。很明显,最终的答案就是f[n][m]。

考虑 第i 种花摆多少盆:

  1. 0盆:f[i−1][j]
  2. 11盆: f[i−1][j−1]
  3. 22盆:f[i−1][j−2]
  4. ...
  5. ai​盆: ]f[i−1][j−ai​],需要保证 j−ai​>=0

所以,可以得到如下状态转移方程:

二、初始状态
知道了状态是什么,就很容易确定初始状态:当i=1,j<=a[1] 时,也就是只有一种花可以摆时,都只能有 1 种方案,j>a[1]时,0 种方案。

参考代码:

#include<bits/stdc++.h>
using namespace std;
int a[110],dp[110][110]; // dp[i][j]: 前i种花,摆j盆的方案数
int sum[110];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum[i] = sum[i-1] + a[i];
    }
    for(int i = 0; i <= min(a[1], m); i++)
        dp[1][i] = 1;
    for(int i=2;i<=n;i++){
        for(int j=0;j<=min(sum[i], m);j++){
            for(int k=0;k<=min(a[i],j);k++){
                dp[i][j] = (dp[i][j] + dp[i-1][j-k]) % int(1e6+7);
            }
        }
    }
    cout<<dp[n][m];
}

2012年NOIP普及组T4-文化之旅

题目描述

有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超过一次(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家)。不同的国家可能有相同的文化。不同文化的国家对其他文化的看法不同,有些文化会排斥外来文化(即如果他学习了某种文化,则他不能到达排斥这种文化的其他国家)。

现给定各个国家间的地理关系,各个国家的文化,每种文化对其他文化的看法,以及这位使者游历的起点和终点(在起点和终点也会学习当地的文化),国家间的道路距离,试求从起点到终点最少需走多少路

输入格式

第一行为五个整数 N,K,M,S,T,每两个整数之间用一个空格隔开,依次代表国家个数(国家编号为 1 到 N),文化种数(文化编号为 1 到 K),道路的条数,以及起点和终点的编号(保证 S 不等于 T);

第二行为 N 个整数,每两个整数之间用一个空格隔开,其中第 i个数 Ci,表示国家 i的文化为 Ci。

接下来的 K 行,每行 K 个整数,每两个整数之间用一个空格隔开,记第 i 行的第 j 个数为 aij,aij= 1 表示文化 i 排斥外来文化 j(i 等于 j 时表示排斥相同文化的外来人),aij= 0 表示不排斥(注意 i 排斥 j 并不保证 j 一定也排斥 i)。

接下来的 M 行,每行三个整数 u,v,d,每两个整数之间用一个空格隔开,表示国家 u与国家 v 有一条距离为 d 的可双向通行的道路(保证 u 不等于 v,两个国家之间可能有多条道路)

输出格式

输出只有一行,一个整数,表示使者从起点国家到达终点国家最少需要走的距离数(如果无解则输出-1

输入输出样例

输入样例1:
2 2 1 1 2
1 2
0 1
1 0
1 2 10
输出样例1:
-1
输入样例2:
2 2 1 1 2 
1 2
0 1
0 0
1 2 10
输出样例2:
10

说明

【输入输出样例说明1】

由于到国家 2 必须要经过国家 1,而国家 2 的文明却排斥国家 1 的文明,所以不可能到达国家 2。

【输入输出样例说明2】

路线为 1 -> 2

【数据范围】

对于 100%的数据,有 2≤N≤100 1≤K≤100 1≤M≤N2 1≤ki≤K 1≤u, v≤N 1≤d≤1000 S≠T 1≤S,T≤N

耗时限制1000ms  内存限制128MB

解析:

考点:图论,最短路-Dijkstra算法,搜索

Dijkstra算法

参考代码

#include<cstring>
#include<cmath>
#include<iostream>
#include<climits>
using namespace std;
const int L=110;
int C[L],vis[L][L],map[L][L],dis[L];// 文化值数组,文化是否冲突数组 地图  出发点到每个点的最短距离数组 
bool b[L]; // 点是否最优判断数组 
int main(){
	memset(map,0,sizeof(map));
	int N,K,M,S,T;
	cin>>N>>K>>M>>S>>T;
	for(int i=1;i<=N;i++)cin>>C[i];
	for(int i=1;i<=K;i++)for(int n=1;n<=K;n++)cin>>vis[i][n];
	for(int i=1;i<=M;i++){
		int x,y,d;
		cin>>x>>y>>d;
		map[x][y]=d;
		map[y][x]=d;
	}
	memset(dis,-1,sizeof(dis));
	//数据初始化
	 
	for(int i=1;i<=N;i++){
		if(i==S)dis[i]=0;
		else if(map[S][i]!=0){
			if(vis[C[i]][C[S]]==0)dis[i]=map[S][i];//初始化 
		}
	}
	b[S]=1;// 初始化 
	
	for(int i=1;i<N-1;i++){
		int xmin=INT_MAX,k=-1;
		for(int n=1;n<=N;n++)if(!b[n]&&xmin>dis[n]&&dis[n]!=-1){
			xmin=dis[n];
			k=n; //挑出的最近的点 
		}
		if(k==-1)continue;
		
		//利用挑出的点进行优化 
		b[k]=1;
		for(int n=1;n<=N;n++)if(!b[n]&&map[n][k]!=0&&vis[C[n]][C[k]]==0){
			if(dis[n]==-1)dis[n]=dis[k]+map[n][k];
			else dis[n]=min(dis[n],dis[k]+map[n][k]);
		}
	}
	cout<<dis[T];
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值