您的位置:首页 > 其它

NOIP模拟题题解

2015-11-01 10:45 423 查看
这套题是一位学长出的=-=关于我们热爱的小机房(但由于强行用梗,部分题意不清),很不幸的是我那天刚好秀逗了,成为了唯一一个爆零的

Day 1:

这天我整个人都是傻的,于是我不出意料的成为唯一一个爆零的= =
1.【真●翻转游戏】
flip.cpp
【题目描述】

“4*4的翻转游戏太简单了”,kkke心想,“如果变成n*n的会怎么样?”

于是kkke找来了一个更大的棋盘(其实就是纸上画的),在棋盘上的每个格子上都放上棋子,每个棋子有黑白两面,最初有的棋子黑色向上,有的白色向上。翻转的规则一样,就是要用最少的翻转次数来使所有棋子向上的都是同一个颜色。每一次翻转,kkke会选择任意一个棋子,将它和上下左右共5个棋子一同翻转。

 

【输入】

第一行:整数n,表示棋盘有n*n个格子

接下来n行:每行包括n个字母,表示初始的每个棋子的状态。w表示这个棋子白色向上, b表示这个棋子当前是黑色向上的。

 

【输出】

输出为一行,如果无法达到目标状态,则输出“Impossible\n”,否则输出一个整数,表示kkke最少需要翻转的次数。

【输入样例】

4

bwwb

bbwb

bwwb

bwww

【输出样例】

4

【数据规模】

30%的数据满足:n<=4

100%的数据满足:n<=16

搜索。先搜索第一层的所有情况,由此判断整个棋盘的情况。代码内有详解。

#include<cstdio>//搜索
#include<algorithm>
#include<cstring>
using namespace std;
const int inf=0x7f7f7f;
char str[20][20];
int map[20][20];
int n;
int work(int c)
{
int q=1<<n;//2^n;因为第一层有n个棋子每个棋子有两种颜色,所以枚举情况为2^n种
int ans=inf;
for(int k=0;k<q;k++)//搜索第一层的所有情况
{
int step=0;//记录操作步数
for(int j=0;j<n;j++)//初始化:将字符转化为数字
{
for(int i=0;i<n;i++)
{
if(str[j][i]=='b') map[j][i]=1;
else map[j][i]=0;
}
}
for(int i=0;i<n;i++)//改变第一层的状态
{
if(k&(1<<i)) continue;////k表示状态,i要和要改变成的颜色一致******
step++;
map[0][i]=1-map[0][i];//取反
if(i>0) map[0][i-1]=1-map[0][i-1];//左边有棋子
if(i<n-1) map[0][i+1]=1-map[0][i+1];//右边有棋子
if(n>1) map[1][i]=1-map[1][i];//下面有棋子
}
for(int i=1;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(map[i-1][j]==c) continue;//如果上一层对应位置不是目的状态
step++;//则将当前位置翻面
map[i][j]=1-map[i][j];
map[i-1][j]=c;//***
if(i>0) map[i][j-1]=1-map[i][j-1];
if(j<n-1) map[i][j+1]=1-map[i][j+1];
if(i<n-1) map[i+1][j]=1-map[i+1][j];//***
}
}
bool flag=true;
for(int i=0;i<n;i++)//判断最后一层是否是目的状态
{
if(map[n-1][i]!=c)
{
flag=false;
break;
}
}
if(flag)
{
ans=min(ans,step);//若最后一层满足则一定到达目的状态
}
}
return ans;
}
int main()
{
freopen("flip.in","r",stdin);
freopen("flip.out","w",stdout);
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s",str[i]);
}
int ans=min(work(1),work(0));
if(ans==inf) printf("Impossible\n");
else printf("%d\n",ans);
return 0;
}


2.【丑数】
humble.cpp
【题目描述】

如果一个数没有2,3,5,7以外的素数因子,那么这个数被称为“丑数”,又称“humble number”。

这里列出前几个“丑数”:1,2,3,4,5,6,7,8,9,10,12,14,15,16,18,20,21,24,25,27。经验证,2000000000以内的“丑数”共有5842个。

现在,icezero想知道第n个丑数(从1开始编号)是多少,但他忙着做智能手环,于是这个简单的问题只有求助各位msoier了。

 

【输入】

第一行:整数n,表示需要查找的“丑数”序号。

以后n行,第i+1行:整数ai,表示需要查找第ai个“丑数”。

 

【输出】

第i行:输出第ai个“丑数”

 

【输入样例】

3

1

11

5842

 

【输出样例】

1

12

2000000000

 

【数据规模】

10%的数据满足:答案<=100,n=1

30%的数据满足:ai<=100,n<=100

100%的数据满足:ai<=5842,n<=1000
简单模拟。。也可以打表

#include<cstdio>//模拟
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int c[]={2,3,5,7};
int n;
int q[1005];
bool v[32][32][32][32];
//int sushu[105];
int t;
int num[6000];//记录丑数的编号;
bool check(long long x)
{
if(x>2000000000||x<0) return 0;
int a=0,b=0,c=0,d=0;
while(x%2==0){
x=x/2;
a++;
}
while(x%3==0)
{
x=x/3;
b++;
}
while(x%5==0)
{
x=x/5;
c++;
}
while(x%7==0)
{
x=x/7;
d++;
}
if(v[a][c][d]) return 0;
else {
v[a][b][c][d]=1;
return 1;
}
}
bool compare(long a,long b)
{
return a<b;
}
void work(long long x)
{
for(int i=0;i<=3;i++)
{
long long a=c[i]*x;

if(!check(a)) continue;
num[++t]=a;
work(a);
}
}
int main()
{
freopen("humble.in","r",stdin);
freopen("humble.out","w",stdout);
scanf("%d",&n);
memset(v,0,sizeof(v));
v[0][0][0][0]=1;
num[1]=1;
t=1;
work(1);
sort(num+1,num+t+1,compare);
for(int i=1;i<=n;i++)
{
scanf("%d",&q[i]);
printf("%d\n",num[q[i]]);
}
return 0;
}


3.【小机房危机】
crisis.cpp
【题目描述】

Stevenzzzk在得知今年的noip要开始的时候,准备会小机房看望并慰问msoiers,他的众多仆人们强烈要求要跟着去服侍他,那么问题来了,小机房怎么能容得下土豪珂的众多仆人呢,不,哪怕是整个绵实也不能,于是聪明的Rain.Xu老师想到了一个办法,选出最优秀的k个仆人分别编号为1~k,首先,第1个仆人陪同Stevenzzzk从豪宅以最短的时间走到小机房,然后在[b]到达时
开始服侍他直到下一个仆人到来(路上有专门的仆人服侍,不算在k个仆人内),所有仆人都想以最短的时间走到小机房,他们同时出发,但走过的路径不能与编号比他小的仆人完全相同,显然,编号越小的仆人越先服侍他(同时到达则一起服侍)。

整个地图有n个点(0~(n-1)),有m条单向路。现在,第k个仆人想知道自己多久才能服侍到Stevenzzzk。

 

【输入】

第一行:整数n,m,k,s(代表豪宅的点的编号),t(代表小机房的点的编号)

接下来m行:每行包括 整数a,b,T,代表从a走到b要消耗时间T;

 

【输出】

输出为一行,如果Stevenzzzk无法到达小机房,则输出“-1\n”否则输出第k个仆人开始服侍Stevenzzzk的时间。

 

【输入样例】

4 810 0 3

0 192

0 268

0 31

1 014

1 267

2 319

3 073

3 228

【输出样例】

142

【数据规模】

有10%的数据满足:k=1

另外30%的数据满足:n<=60

100%的数据满足:n<=1000,m<=100000,k<=1000,s!=t
启发式搜索+最短路 。然而当时的我并不知道什么事启发式搜索_(:з」∠)_,所以当时我根本不会做,下来过后结合标程然后查了资料才知道。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>

#define MAXN 1010
#define MAXM 100010
#define INF 0x3f3f3f3f

using namespace std;

int n,m,K,s,t;
int dist[MAXN];
int hehe[MAXN];
int cnt[MAXN];

struct to_edge{
int to;
int next;
int v;
}tedge[MAXM];

struct from_edge{
int from;
int next;
int v;
}fedge[MAXM];

int thead[MAXN],fhead[MAXN],top;

queue<int>q;

struct step{
int p,v;
bool operator<(const step &a)const
{
return dist[p]+v>dist[a.p]+a.v;
}
}nows,nexts;

priority_queue<step>p_q;

int read();

void add(int a,int b,int v)
{
tedge[top].to=b;
tedge[top].v=v;
tedge[top].next=thead[a];
thead[a]=top;

fedge[top].from=a;
fedge[top].v=v;
fedge[top].next=fhead;
fhead[b]=top++;
}

void read_data()
{
n=read();
m=read();
K=read();
s=read();
t=read();
if(s==t)K++;
for(int i=0;i<=n;i++)thead[i]=fhead[i]=-1;
for(int i=0;i<=n;i++)dist[i]=INF;
int a,b,v;
for(int i=0;i<m;i++)
{
a=read();
b=read();
v=read();
add(a,b,v);
}
}

void spfa()
{
dist[t]=0;
q.push(t);
while(!q.empty())
{
int nowp=q.front();q.pop();
hehe[nowp]=false;
for(int i=fhead[nowp];i!=-1;i=fedge[i].next)
{
if(dist[fedge[i].from]>dist[nowp]+fedge[i].v)
{
dist[fedge[i].from]=dist[nowp]+fedge[i].v;
if(hehe[fedge[i].from])continue;
q.push(fedge[i].from);
hehe[fedge[i].from]=true;
}
}
}
}

int astar()
{
nows.p=s;
nows.v=0;
p_q.push(nows);
while(!p_q.empty())
{
nows=p_q.top();p_q.pop();
for(int i=thead[nows.p];i!=-1;i=tedge[i].next)
{
nexts.p=tedge[i].to;
nexts.v=nows.v+tedge[i].v;
cnt[nexts.p]++;
if(nexts.p==t&&cnt[t]==K)return nexts.v;
if(cnt[nexts.p]<=K)p_q.push(nexts);
}
}
return -1;
}

int main()
{
freopen("crisis.in","r",stdin);
freopen("crisis.out","w",stdout);
read_data();
spfa();
if(dist[s]==INF)printf("-1\n");
else printf("%d\n",astar());
return 0;
}

int read()
{
int a=0;
char c;
while((c=getchar())<'0'||c>'9');

d187
do{
a=a*10+c-'0';
}while((c=getchar())>='0'&&c<='9');
return a;
}

Day 2:

于是第二天我决定虐场=-=。。。

1.【堆快递】

express.cpp
【题目描述】

CharlieYoung即使去送快递了,还是不忘他以前创造的模拟的神话,于是他找来了一些大小相同的长方体快递,并将这些快递堆放在一个n*m的矩形区域内。当然他找不到这么多相同的快递,他只是想用程序模拟而已。

在程序中每个快递用符号表示方法:每个顶点用1个加号’+’表示,长用3个’-’表示,宽用1个’/’,高用两个’|’表示。字符’+’和’-’和’/’ 和’|’的ASCII码分别为43,45,47,124。字符’.’(ASCII码46)需要作为[b]背景
输出,即立体图里的空白部分需要用’.’来代替

即每个快递表示为



需要模拟的是:堆好的快递的立体图的样子

 

【输入】

第一行:整数n,m,表示场地为n*m的

接下来n行:每行包括m个整数,第i行第j个数表示(i,j)处的快递层数

 

【输出】

输出为一行,如果无法达到目标状态,则输出“Impossible\n”,否则输出一个整数,表示kkke最少需要翻转的次数。

【输入样例】

3 4

2 21 2

2 21 1

3 21 2

 
 

【输出样例】



【数据规模】

有10%的数据满足:n=1;

另10%的数据满足:m=1;

另10%的数据满足:最高层数=1;

100%的数据满足:1<=m,n<=50,最高层数<=100

直接模拟,不说了= =,最开始先把背景图建好(即一个全是‘.’的图),然后通过n和m的关系来确定图的大小。
ps:= =本人代码比较粗暴,如要往下阅读请做好心理准备。。。
#include<algorithm>//【模拟】从前往后填充 从左往右填
#include<cstdio>
using namespace std;
int m;
int n;
char a[500][500];
int map[51][51];
int map1[51][51];
int maxh=-1;
int maxw=-1;
int main()
{
freopen("express.in","r",stdin);
freopen("express.out","w",stdout);
scanf("%d%d",&m,&n);
for(int i=m;i>=1;i--)
for(int j=n;j>=1;j--)
{
scanf("%d",&map1[i][j]);
maxh=max(maxh,map1[i][j]*3+(i-1)*2+3);
map[i][n+1-j]=map1[i][j];
}
maxw=n*4+1+m*2;
for(int i=1;i<=maxh;i++)
{
for(int j=1;j<=maxw;j++)
{
a[i][j]=46;
}
}
for(int i=m;i>=1;i--)
{
for(int j=1;j<=n;j++)
{
if(map[i][j]!=0)
{
int k=3;
a[maxh-(i-1)*2][j*4-4+1+(i-1)*2]='+';
a[maxh-(i-1)*2][j*4-4+2+(i-1)*2]='-';
a[maxh-(i-1)*2][j*4-4+3+(i-1)*2]='-';
a[maxh-(i-1)*2][j*4-4+4+(i-1)*2]='-';
a[maxh-(i-1)*2][j*4-4+5+(i-1)*2]='+';//
a[maxh-(i-1)*2-1][j*4-4+1+(i-1)*2]='|';
a[maxh-(i-1)*2-1][j*4-4+2+(i-1)*2]=' ';
a[maxh-(i-1)*2-1][j*4-4+3+(i-1)*2]=' ';
a[maxh-(i-1)*2-1][j*4-4+4+(i-1)*2]=' ';
a[maxh-(i-1)*2-1][j*4-4+5+(i-1)*2]='|';
a[maxh-(i-1)*2-1][j*4-4+6+(i-1)*2]='/';
map[i][j]--;
while(map[i][j]>0)
{
a[maxh-(i-1)*2-k+1][j*4-4+1+(i-1)*2]='|';
a[maxh-(i-1)*2-k+1][j*4-4+2+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+3+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+4+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+5+(i-1)*2]='|';
a[maxh-(i-1)*2-k+1][j*4-4+6+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+7+(i-1)*2]='+';
k++;
a[maxh-(i-1)*2-k+1][j*4-4+1+(i-1)*2]='+';
a[maxh-(i-1)*2-k+1][j*4-4+2+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+3+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+4+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+5+(i-1)*2]='+';
a[maxh-(i-1)*2-k+1][j*4-4+6+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+7+(i-1)*2]='|';
k++;
a[maxh-(i-1)*2-k+1][j*4-4+1+(i-1)*2]='|';
a[maxh-(i-1)*2-k+1][j*4-4+2+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+3+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+4+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+5+(i-1)*2]='|';
a[maxh-(i-1)*2-k+1][j*4-4+6+(i-1)*2]='/';
a[maxh-(i-1)*2-k+1][j*4-4+7+(i-1)*2]='|';
k++;
map[i][j]--;
}
a[maxh-(i-1)*2-k+1][j*4-4+1+(i-1)*2]='|';
a[maxh-(i-1)*2-k+1][j*4-4+2+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+3+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+4+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+5+(i-1)*2]='|';
a[maxh-(i-1)*2-k+1][j*4-4+6+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+7+(i-1)*2]='+';
k++;
a[maxh-(i-1)*2-k+1][j*4-4+1+(i-1)*2]='+';
a[maxh-(i-1)*2-k+1][j*4-4+2+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+3+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+4+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+5+(i-1)*2]='+';
a[maxh-(i-1)*2-k+1][j*4-4+6+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+7+(i-1)*2]='|';
k++;
a[maxh-(i-1)*2-k+1][j*4-4+2+(i-1)*2]='/';
a[maxh-(i-1)*2-k+1][j*4-4+3+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+4+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+5+(i-1)*2]=' ';
a[maxh-(i-1)*2-k+1][j*4-4+6+(i-1)*2]='/';
a[maxh-(i-1)*2-k+1][j*4-4+7+(i-1)*2]='|';
k++;
a[maxh-(i-1)*2-k+1][j*4-4+3+(i-1)*2]='+';
a[maxh-(i-1)*2-k+1][j*4-4+4+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+5+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+6+(i-1)*2]='-';
a[maxh-(i-1)*2-k+1][j*4-4+7+(i-1)*2]='+';
}
}
}
for(int i=1;i<=maxh;i++)
{
for(int j=1;j<=maxw;j++)
{
printf("%c",a[i][j]);
}
printf("\n");
}
return 0;
}


2.【骗分】
cheat.cpp
【题目描述】

return0可是骗分界的高手,曾经通过输出”1\n”就拿下了40%的数据,而今天他要做的题可不是仅仅输1那么简单,经过他高端的分析,发现有n和k两个输入特别显眼,在多次测试后,发现了一个骗分的方法,在1~n共n个数中取k个出来,计算这些数的最大公约数(设1个数的最大公约数是其本身),当这k个数的最大公约数在所有取法中最大的时候。将这个最大的最大公约数输出,居然能够AC那道难题,小机房的伙伴们都惊叹于return0的骗分之术。但传奇的背后,还有一个问题,虽然这种骗分法比直接做原题简单,但怎么才能求出最大公约数最大的k个数的组合呢?

 

【输入】

第一行:整数n,k

 

【输出】

第一行:输出找出这k个数的最大公约数

 

【输入样例】

4 2

 

【输出样例】

2

 

【数据规模】

10%的数据满足:1<=k<=n<=10

40%的数据满足:1<=k<=n<=106

100%的数据满足:1<=k<=n<=1066

通过证明我们可以知道这道题其实就是用n/k就可以了= =(这里就不给出证明了,因为我是看出来的=-=)
我们十分单纯的学长(出题人)下来还一脸委屈的说,_(:з」∠)_他还想把这道题当做压轴题呢,结果被机智的我们一秒看破_(:з」∠)_。
首先看到这个数据。。。= =1066,整个人都是醉的啊。。
所以这道题就转化为了高精除高精:
#include<cstdio>//高精除高精 n/k
#include<algorithm>
#include<cstring>
using namespace std;
int a[201];
int b[201];
int c[201];

void init(int a[])//字符串转换
{
char c[100];
scanf("%s",c);
a[0]=strlen(c);//记录长度
for(int i=1;i<=a[0];i++)
{
a[i]=c[a[0]-i]-'0';
}
}
void printf(int a[])//输出转化
{
if(a[0]==0) {printf("0\n");
return;
}
for(int i=a[0];i>=1;i--)
{
printf("%d",a[i]);
}
printf("\n");
return;
}
int compare(int a[],int b[])//比较
{
if(a[0]>b[0]) return 1;
if(a[0]<b[0]) return -1;
//如果位数相同 比较每一位
for(int i=a[0];i>=1;i--)
{
if(a[i]>b[i]) return 1;
if(a[i]<b[i]) return -1;
}
return 0;
}
void jian(int a[],int b[])//做减法
{
int flag=compare(a,b);
if(flag==0) {//
a[0]=0;
return;
}
if(flag==1)
{
for(int i=1;i<=a[0];i++)
{
if(a[i]<b[i])
{
a[i+1]--;
a[i]+=10;
}
a[i]-=b[i];
}

while(a[0]>0&&a[a[0]]==0) a[0]--;
return;
}
}
void work(int p[],int q[],int d)
{
for(int i=1;i<=p[0];i++)
{
q[i+d-1]=p[i];
}
q[0]=p[0]+d-1;
}
void chu(int a[],int b[],int c[])//不断地减,直到减到小于除数,然后减的次数加起来,就是商
{
int t[101];
c[0]=a[0]-b[0]+1;
for(int i=c[0];i>=1;i--)
{
memset(t,0,sizeof(t));
work(b,t,i);
while(compare(a,t)>=0) {
c[i]++;
jian(a,t);
}
}
while(c[0]>0&&c[c[0]]==0) c[0]--;
return;
}
int main()
{
freopen("cheat.in","r",stdin);
freopen("cheat.out","w",stdout);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
init(a);
init(b);
chu(a,b,c);
printf(c);
return 0;
}

3.【矩阵乘法】
matrix.cpp
【题目描述】

在矩阵乘法中,若要使矩阵A和B可乘,需满足矩阵A的列数等于矩阵B的行数。,例如:若A是一个p×q的矩阵,B是一个q×r的矩阵,则A,B可乘,其乘积C=A×B是一个p×r的矩阵。

计算矩阵乘法C=A×B的常规方法是:

for(inti=0;i<p;i++)

{

    for(int j=0;j<r;j++)

{

    C[i][j]=0;

    for(intk=0;k<q;k++)C[i][j]+=A[i][k]*B[k][j];

}

}

现在,刘莫意又开始莫得意思了,他想知道n个矩阵的连乘用这种方法最少需要计算多少次乘法。

 

【输入】

第一行:整数n,代表矩阵的个数

接下来n行:每行包括 整数ai,bi 代表第i个矩阵有ai行,bi列(保证相邻矩阵之间可乘)。

 

【输出】

输出为一行,一个整数 表示计算最少需要的乘法次数;

 

【输入样例】

2

1 2

2 3

 

【输出样例】

6

 

【数据规模】

有10%的数据满足:n=2

100%的数据满足:n<=10, ai,bi<=100

这道题就无耻了_(:з」∠)_,打着矩阵乘法的旗号来考简单dp,机房里一位妹子下来知道这是dp过后感觉痛不欲生=-=
(因为她看到矩阵乘法就觉得不会于是直接放弃_(:з」∠)_)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
int a[11];
int b[11];
int dp[11][11];

int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
scanf("%d",&n);
memset(dp,0x7f7f7f,sizeof(dp));
for(int i=1;i<=n;i++)
{
scanf("%d %d",&a[i],&b[i]);
dp[i][i]=0;
}
for(int i=n-1;i>=1;i--)//枚举每个矩阵的起点
{
for(int j=i+1;j<=n;j++)//枚举终点
{
for(int k=i;k<=j-1;k++)//中间节点
{
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*b[j]);
}
}
}
printf("%d",dp[1]
);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: