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 种花摆多少盆:
- 0盆:f[i−1][j]
- 11盆: f[i−1][j−1]
- 22盆:f[i−1][j−2]
- ...
- 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;
}