NOIP2012普及组解题报告
by RtPYH
------------------------------------------------------------------------------------------------------------------
前言:作者是一个蒟蒻,假设对本文有建议,欢迎提出!鄙人将虚心接受。
-------------------------------------------------------------------------------------------------------------------
1.质因数分解
【题目描写叙述】
给你一个由两个质数相乘得到的正整数n。求较大的质数
【数据范围】
n小于等于2*10^9
【分析】
当时有几个同学写TLE了..后来才发现他们读题不细致:n已经约定为两个质数的乘积。
所以我们从2枚举到根号n,仅仅要n%i==0那就输出n/i
时间复杂度(根号N)
【代码】
#include <iostream> using namespace std; #include <cstdio> int n,i; int main(){ freopen("prime.in","r",stdin); freopen("prime.out",'w",stdout); cin>>n; for(i=2;i*i<=n;i++) if(n%i==0){cout<<n/i;break;} return 0; }
2.寻宝
【题目描写叙述】
传说非常遥远的藏宝楼顶层藏着诱人的宝藏。 小明历尽千辛万苦最终找到传说中的这个藏
宝楼,藏宝楼的门口竖着一个木板。上面写有几个大字:寻宝说明书。说明书的内容例如以下:
藏宝楼共同拥有 N+1 层。最上面一层是顶层, 顶层有一个房间里面藏着宝藏。 除了顶层外,
藏宝楼另有 N 层, 每层 M 个房间。这 M 个房间围成一圈并按逆时针方向依次编号为 0。…,
M-1。当中一些房间有通往上一层的楼梯。每层楼的楼梯设计可能不同。每一个房间里有一个
指示牌,指示牌上有一个数字 x。 表示从这个房间開始按逆时针方向选择第 x 个有楼梯的房
间(假定该房间的编号为 k) 。从该房间上楼,上楼后到达上一层的 k 号房间。比方当前房
间的指示牌上写着 2,则按逆时针方向開始尝试,找到第 2 个有楼梯的房间,从该房间上楼。
假设当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。
寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示
牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥” 。
宝楼,藏宝楼的门口竖着一个木板。上面写有几个大字:寻宝说明书。说明书的内容例如以下:
藏宝楼共同拥有 N+1 层。最上面一层是顶层, 顶层有一个房间里面藏着宝藏。 除了顶层外,
藏宝楼另有 N 层, 每层 M 个房间。这 M 个房间围成一圈并按逆时针方向依次编号为 0。…,
M-1。当中一些房间有通往上一层的楼梯。每层楼的楼梯设计可能不同。每一个房间里有一个
指示牌,指示牌上有一个数字 x。 表示从这个房间開始按逆时针方向选择第 x 个有楼梯的房
间(假定该房间的编号为 k) 。从该房间上楼,上楼后到达上一层的 k 号房间。比方当前房
间的指示牌上写着 2,则按逆时针方向開始尝试,找到第 2 个有楼梯的房间,从该房间上楼。
假设当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。
寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示
牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥” 。
请帮助小明算出这个打开宝箱的密钥
【数据范围】
对于 100%数据,有 0<N≤10000,0<M≤100。0<x≤1,000,000
【分析】
还是比較easyTLE的题目,关键考察是否能读题。分析题目。首先,一般的模拟思路能够得出:
for(i=1 -) n)
for(;tot<=i;p++)
{ if(p的位置有楼梯)tot++;
p%=m;
}
累计答案
输出
上述代码时间复杂度O(NX)。期望得分50分
肯定是TLE的,我们发现数据范围有规律:m<=x
我们又发现。因为是转圈。所以我们并不须要转x圈,仅仅须要转x mod m圈就可以,上述代码时间复杂度为:
O(NM)
完了吗?不!我们发现,x mod m有可能为0,所以要特判。假设x mod m=0,那么x=m
期望得分100分
【代码】
#include <iostream> #include <cstdio> using namespace std; const int MaxN=10010; const int MaxM=101; const int Mod=20123; int build[MaxN][MaxM][2],n,t[MaxN],m; int ans=0; int main(){ freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); scanf("%d %d",&n,&m); int tot,i,j,k,ii; for(i=1;i<=n;i++) for(j=0;j<m;j++) { scanf("%d %d",&build[i][j][0],&build[i][j][1]); if(build[i][j][0]==1)t[i]++; } scanf("%d",&k); for(i=1;i<=n;i++){ tot=0; ans=(ans+build[i][k][1])%Mod; ii=build[i][k][1]%t[i]; if(ii==0)ii=t[i]; for(;tot<ii;k++){ k%=m; if(build[i][k][0]==1)tot++; } k=(k-1+m)%m; } printf("%d",ans); return 0; }
3.摆花
【题目描写叙述】
给你n朵花可摆放的盆数以及总共能摆放的盆数。求摆放方案(同种必须相邻)
【数据范围】
n小于等于100
【分析】
阶段:背包问题
状态:f(i,j)表示第i种花摆入容量为j的背包所能得到的方案数
OK!
【代码】
</pre></div><pre name="code" class="cpp">#include <iostream> using namespace std; #include <cstdio> #include <cstring> const int MaxN=1001; const int Mod=1000007; int a[MaxN],n,m; int f[MaxN][MaxN],k; void init(){ freopen("flower.in","r",stdin); freopen("flower.out","w",stdout); scanf("%d %d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); } void DP(){ int i,j,k; //f(i,j)表示前i朵花k盆放入容量为j的背包 f[0][0]=1; for(i=1;i<=n;i++) for(j=0;j<=m;j++) for(k=0;k<=j && k<=a[i];k++){ f[i][j]+=f[i-1][j-k]; f[i][j]%=Mod; } cout<<f[n][m]; } int main(){ init(); DP(); return 0; }
4.文化之旅
【题目描写叙述】
有一位使者要游历各国,他每到一个国家。都能学到一种文化,但他不愿意学习不论什么一
种文化超过一次(即假设他学习了某种文化,则他就不能到达其它有这样的文化的国家) 。
种文化超过一次(即假设他学习了某种文化,则他就不能到达其它有这样的文化的国家) 。
不
同的国家可能有同样的文化。 不同文化的国家对其它文化的看法不同,有些文化会排斥外来
文化(即假设他学习了某种文化,则他不能到达排斥这样的文化的其它国家) 。
现给定各个国家间的地理关系。 各个国家的文化, 每种文化对其它文化的看法。 以及这
位使者游历的起点和终点(在起点和终点也会学习当地的文化),国家间的道路距离,试求
从起点到终点最少需走多少路。
【数据范围】
n小于等于100
【分析】
对于求最短路问题。经常使用的算法有SPFA和Dijkstra以及Bellman_Ford。因为N的范围太小。且有
很多限制,所以採用floyd算法求解
我们开一个vis数组,vis[i][j]表示国家I和国家J的兼容情况。然后依据这个。来跑一遍floyd
就可以出解
题外话:当时比赛有神牛用搜索外加最优性剪枝AC了。蒟蒻想学习一下,望大牛能评论
【代码】
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MaxN=101; const int oo=1<<25; int c[MaxN],a[MaxN][MaxN],d[MaxN][MaxN]; int n,kd,m,s,t; bool vis[MaxN][MaxN]; int main() { freopen("culture.in","r",stdin); freopen("culture.out","w",stdout); scanf("%d%d%d%d%d",&n,&kd,&m,&s,&t); int c1,c2,i,j,k,u,v,cap; memset(d,0,sizeof(d)); memset(vis,false,sizeof(vis)); for(i=1;i<=n;i++)scanf("%d",&c[i]); for(i=1;i<=kd;i++) for(j=1;j<=kd;j++) scanf("%d",&a[i][j]); for(i=1;i<=n;i++) for(j=1;j<=n;j++) d[i][j]=oo; for(i=1;i<=n;i++) for(j=1;j<=n;j++) { c1=c[i],c2=c[j]; if(a[c1][c2]==0)vis[j][i]=true; } for(i=1;i<=m;i++){ scanf("%d %d %d",&u,&v,&cap); if(vis[u][v] && cap<d[u][v]) d[u][v]=cap; if(vis[v][u] && cap<d[v][u]) d[v][u]=cap; } for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(vis[i][k]&&vis[k][j]&&d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j]; if(d[s][t]>=oo)cout<<-1; else cout<<d[s][t]; return 0; }