纪中集训 Day21&Day22
T1:
纯粹蒙分
T2:
少了个判断…
T3:
不会做,没思路
T4:
辛辛苦苦打了2个多小时,才50
T5:
与我而言不可做之题
T1
题目:
味味有一个A×B×C 的长方体积木,积木是有1×1×1 的小积木块组成的。我们设定这个长方体的高为A,宽为B,长为C。(为方便起见,长方体的长不一定要比宽的数值大)。
现在味味在这个长方体中的的左上角挖去了一个(A-1)×(B-2)×(C-2)的小长方体。并且告诉你被挖去长方体的体积为n,即n=(A-1)×(B-2)×(C-2)。现在问你,被挖去小长方体后,原有长方体积木中剩下的1×1×1的小积木块最少和最多分别是多少个。也就是说,在告诉你n值的前提下,求min{A×B×C-n}和max{A×B×C-n}。
输入
输入文件名为 block.in。
输入共1行,仅一个正整数n。
输出
输出文件名为 block.out。
输出共1行包含两个用空格隔开的正整数,依次表示最少剩余小积木块和最多剩余小积木块个数。
样例:
input 1
4
input 2
7
output 1
28 41
output 2
47 65
样例1说明:
4=(2-1)×(4-2)×(4-2) 最少剩余的小积木块为2×4×4-4=28(此时A,B,C值分别为2,4,4)
4=(5-1)×(3-2)×(3-2) 最多剩余的小积木块为5×3×3-4=41(此时A,B,C值分别为5,3,3)
数据范围限制:
对于20%的数据1 ≤n≤400
对于50%的数据1 ≤n≤10^6
对于100%的数据1 ≤ n≤10^9
解题思路:
求出n所有的因数
然后两两枚举
如果n能整除它们的积
比较它们积的值和当前最小值
最后的最小值还要减
n
n
n
最大值的求法
因为挖去的是(
A
A
A-1)×(
B
B
B-2)×(
C
C
C-2)
化简一下就是(
n
n
n+1) * (1+2) * (1+2)
最后也要减
n
n
n
代码:
#include<iostream>
#include<cstdio>
using namespace std;
long long n,x,y,t,z,s=2147483647,a[1100];
int main()
{
//freopen("block.in","r",stdin);
//freopen("block.out","w",stdout);
scanf("%d",&n);
int j=1;
while (j*j<=n)
{
if (n%j==0)
{
a[++t]=j;
if (n/j!=j)
a[++t]=n/j;
}
j++;
} //找因数
for (int i=1;i<=t;i++)
for (int j=1;j<=t;j++)
if ((n/a[i]/a[j])*a[i]*a[j]==n) //能整除
{
x=a[i]+2;
y=a[j]+2;
z=n/a[i]/a[j]+1;
s=min(s,x*y*z); //更新最小值
}
s=s-n; //减n
cout<<s<<" ";
x=(n+1)*9-n; //套公式
cout<<x<<endl;
fclose(stdin);
fclose(stdout);
return 0;
}
T2
题目:
Windows中的扫雷游戏是大家都熟悉的小游戏,今天,味味也设计了一个简易的扫雷游戏。味味设计的扫雷游戏功能如下:
一、程序一开始会读入扫雷区域大小n,表示游戏区域有n*n个小方格组成,接下来会读入n行信息,每行有n个整数(每个整数可能是0,也可能是1),每两个整数之间用一个空格分隔。其中0 所在位置表示该小方格内没有地雷,1 所在位置表示该小方格内有地雷(游戏开始时,扫雷区域中必定包含至少一个地雷)。接下来每行输入两个用空格分开的正整数i和j,每一行的一对i和j表示用户用鼠标单击扫雷区域中第i 行第j 列位置上的小方格(就象我们使用Windows 中扫雷游戏一样),i 和j 表示的位置必定在扫雷区域内。程序每输入一对i和j,就马上进行相应的处理(就象我们在Windows 中鼠标单击某个小方块就会出现结果一样)。
二、程序将根据读入的一组i 和j的值来对扫雷区域作相应处理,具体的处理规则如下:
(1)如果i和j 表示的小方格内没有地雷、而且也没有被处理过(就是第i行第j 列的数值是0),那么将以该小方格为中心的一个正方形区域内所有没有地雷的小方格都赋值为-1(表示该区域的地砖已经被掀开了)。如果在当前正方形区域内有一个位置号为i1和j1(注意:i1<>i并且j1<>j)的小方格内恰好有地雷,则此地雷就被顺利扫除,将该位置标记为-2。如果该正方形区域内某些小方格已经被处理过,则对这些小方格不再做任何处理。举个例子来说明一下,假如输入信息如上图左边所示,那么处理结果就如右边所示。
(2)如果i 和j 表示的小方格已经被处理过(就是第i 行第j 列的数值是-1 或者是-2),那么不作任何处理,继续去读取下一行的i和j 的值。
(3)如果i和j 表示的小方格刚好有地雷,并且该小方格没有被处理过(就是第i 行第j 列的数值是1),那么表示用户触雷,马上输出信息“GAME OVER!”,程序结束。
三、如果在读入i和j的过程中一直没有触雷,那么就一直按照位置信息处理下去,直到满足下列条件之一,就输出相应信息并结束程序:
(1)读入的i和j的值都是0(表示用户不再在某个小方格内单击鼠标右键了),则输出处理后整个扫雷区域的状态(就是输出n行n列的方阵,每行中两个整数之间用一个空格分隔,末尾没有多余空格),然后程序结束。
(2)如果某次处理完后,游戏区域内所有的地雷都被扫除了,那么不必再读入下一行的信息,输出信息“YOU ARE WINNER!”,程序结束。
输入
输入文件名为mine.in。第一行一个整数n(n<=50),接下来是一个n*n 的方阵。再接下来是若干行,每行空格分隔的两个整数,表示i和j,以0 0结束。
输出
输出文件名为mine.out。包含一行,可能输出“YOU ARE WINNER!”,可能输出“GAME OVER!”,
也可能输出一个处理后的方阵。
样例:
input 1
6
0 0 0 0 0 0
0 0 1 0 0 0
1 0 0 0 1 0
0 0 0 0 0 0
0 1 0 0 0 1
0 0 0 0 0 0
1 1
3 4
5 5
4 6
5 2
2 3
0 0
input 2
6
0 0 0 0 0 0
0 0 1 0 0 0
1 0 0 0 1 0
0 0 0 0 0 0
0 1 0 0 0 1
0 0 0 0 0 0
1 1
3 4
5 5
4 6
2 3
0 0
output 1
GAME OVER!
output 2
-1 -1 0 0 0 0
-1 -1 -2 -1 -1 0
1 0 -1 -1 -2 0
0 0 -1 -1 -1 -1
0 1 0 -1 -1 -2
0 0 0 -1 -1 -1
解题思路:
纯模拟
代码:
#include<iostream>
#include<cstdio>
using namespace std;
int fx[10]={0,-1,0,1,-1,0,1,-1,0,1},fy[10]={0,-1,-1,-1,0,0,0,1,1,1};
int a[70][70],n,num,x,y;
int main()
{
//freopen("mine.in","r",stdin);
//freopen("mine.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
if (a[i][j]==1)
num++;
}
scanf("%d%d\n",&x,&y);
while (x!=0&&y!=0) //还有操作指令
{
if (a[x][y]<0) //当前点已被处理过
{
scanf("%d%d",&x,&y);
continue;
}
if (a[x][y]==1) //触雷
{
cout<<"GAME OVER!"<<endl;
return 0;
}
for (int i=1;i<=9;i++)
if (a[x+fx[i]][y+fy[i]]>=0) //改变九宫格内未改变的
{
if (a[x+fx[i]][y+fy[i]]==1)
{
a[x+fx[i]][y+fy[i]]=-2;
num--;
if (num==0) //已经没雷了
{
cout<<"YOU ARE WINNER!"<<endl;
return 0;
}
}
else a[x+fx[i]][y+fy[i]]=-1;
}
scanf("%d%d",&x,&y);
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
printf("%d ",a[i][j]);
printf("\n");
}
fclose(stdin);
fclose(stdout);
return 0;
}
T3
题目:
味味最近对树很感兴趣,什么是树呢?树就是有n个点和n-1条边形成的无环连通无向图。
今年2012年浙江省队选拔赛中味味发现了一个树中最长链(就是树当中距离最远的点对)试题,于是她着手对树进行了一些研究和思考。
味味在研究过程中想知道,对于一个无根树,当节点i作为根的时候树的高是多少。所谓树高指的是从根节点出发,到离根节点最远叶子节点所经过的节点的总数,详见输入输出样例1。
味味现在遇到了一些烦心的事情,不想再继续思考了,请你帮助她解决这个问题。
输入:
共 N 行。第一行为一个正整数 N,表示树的节点个数。第2 行到第 N行里,每行两个用空格隔开的正整数a 和b,表示a 与b有连边。
输出:
共N 行,第i行表示以节点i为根时的树高。
样例:
input 1
3
1 2
2 3
input 2
4
1 4
2 4
3 4
output 1
3
2
3
output 2
3
3
3
2
数据范围限制:
对于30%的数据有 N≤ 100。
对于 60%的数据有 N≤ 300。
对于 100%的数据有 1≤N≤1000,1≤a,b≤N
解题思路:
求树的直径后再一次BFS
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct hhx{
int to,next;
}m[2200];
int f[2200],d[2200],da[2200],head[2200];
int x,y,n,t;
void add(int x,int y)
{
m[++t].to=y;
m[t].next=head[x];
head[x]=t;
}
int bfs(int w)
{
int s;
d[w]=1;
f[1]=w;
int h=0,t=1;
do{
h++;
for (int i=head[f[h]];i;i=m[i].next)
if (d[m[i].to]<0)
{
d[m[i].to]=d[f[h]]+1;
f[++t]=m[i].to;
s=m[i].to;
}
}while(h<t);
return s;
}
void bfs2(int w)
{
da[w]=1;
f[1]=w;
int h=0,t=1;
do{
h++;
for (int i=head[f[h]];i;i=m[i].next)
if (da[m[i].to]<0)
{
da[m[i].to]=da[f[h]]+1;
f[++t]=m[i].to;
if (da[m[i].to]>d[m[i].to])
d[m[i].to]=da[m[i].to];
}
}while(h<t);
}
int main()
{
//freopen("tree.in","r",stdin);
//freopen("tree.out","w",stdout);
memset(d,-1,sizeof(d));
memset(da,-1,sizeof(da));
scanf("%d",&n);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
x=bfs(1);
memset(f,0,sizeof(f));
memset(d,-1,sizeof(d));
y=bfs(x); //找树的直径
bfs2(y); //再广搜一次
for (int i=1;i<=n;i++)
printf("%d\n",d[i]);
fclose(stdin);
fclose(stdout);
}
T4
题目:
4和7是味味的幸运数字。幸运数是那些只由幸运数字组成的正整数。如47,477是幸运数,而5,17,417 就不是幸运数。
定义next(x)为大于或等于x的最小的幸运数。
味味对以下表达式的值很感兴趣 :
next(L)+next(L+1)+…+next(R-1)+next®。
现在告诉你L和R的值,希望你能帮助味味计算出这个表达式的值。
输入:
输入文件sum.in仅一行包含两个正整数L和R(1≤L≤R≤10^9 ),L和R的值之间用一个空格分隔。
输出:
输出文件sum.out 只有一行一个整数,表示表达式的值。
样例:
input 1
2 7
input 2
7 7
output 1
33
样例 1 说明:
next(2)+next(3)+next(4)+next(5)+next(6)+next(7)=4+4+4+7+7+7=33
output 2
7
样例 2 说明:
next(7)=7
数据范围限制:
对于 20%的数据,1≤L≤R≤1000
对于 40%的数据,1≤L≤R≤10^6
另有20%的数据,L=R
对于 100%的数据,1≤L≤R≤10^9
解题思路:
预处理出所有幸运数
由于一个区间内都是同一个幸运数
这个幸运数乘区间长度
最后累加即可
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long a,ans,l,r,x,y,t,jf,len,len2,h[52000];
void dfs(long long w,long long s)
{
if (w==0)
{
h[++t]=s;
return;
}
dfs(w-1,s*10+4);
dfs(w-1,s*10+7);
}
int main()
{
//freopen("sum.in","r",stdin);
//freopen("sum.out","w",stdout);
scanf("%lld%lld",&l,&r);
for (int i=1;i<=12;i++)
dfs(i,0); //预处理幸运数,i是位数
long long j=1,i=l;
while (l>h[j])
j++; //找到符合条件的最小幸运数
while (i<=r)
{
x=min(h[j]-i,r-i)+1; //区间长度
jf+=h[j]*x; //相乘,累加
i=i+x; //下一个区间
j++; //下一个幸运数
}
printf("%lld",jf);
fclose(stdin);
fclose(stdout);
return 0;
}
T5
题目:
给出如下定义:
1. 子矩阵: 从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序) 被称为原矩阵的一个子矩阵。
例如,下面左图中选取第 2、 4 行和第 2、 4、 5 列交叉位置的元素得到一个 2*3 的子矩阵如右图所示。
2. 相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的。
3. 矩阵的分值: 矩阵中每一对相邻元素之差的绝对值之和。
本题任务:给定一个 n 行 m 列的正整数矩阵,请你从这个矩阵中选出一个 r 行 c 列的子矩阵,使得这个子矩阵的分值最小,并输出这个分值。
输入:
输入文件名为 submatrix.in。
第一行包含用空格隔开的四个整数 n, m, r, c,意义如问题中所述,每两个整数之间用一个空格隔开。
接下来的 n 行, 每行包含 m 个用空格隔开的整数,用来表示问题中那个 n 行 m 列的矩阵。
输出:
输出文件名为 submatrix.out。
输出共 1 行,包含 1 个整数,表示满足题目描述的子矩阵的最小分值。
样例:
input1
5 5 2 3
9 3 3 3 9
9 4 8 7 4
1 7 4 6 6
6 8 5 6 9
7 4 5 6 1
input2
7 7 3 3
7 7 7 6 2 10 5
5 8 8 2 1 6 2
2 9 5 5 6 1 7
7 9 3 6 1 7 8
1 9 1 4 7 8 8
10 5 9 1 1 8 10
1 3 1 5 4 8 6
output1
6
output2
16
数据范围限制:
对于 50%的数据, 1 ≤ m≤ 12 , 1 ≤n ≤ 12 , 矩阵中的每个元素 1 ≤ a [i , j] ≤20;
对于 100%的数据, 1 ≤m ≤ 16 , 1 ≤ n≤ 16 , 矩阵中的每个元素 1 ≤ a [i , j] ≤1000,1 ≤ r<=n ,1<=c<=m .
提示:
样例 1 说明
该矩阵中分值最小的 2 行 3 列的子矩阵由原矩阵的第 4 行、第 5 行与第 1 列、第 3 列、第 4 列交叉位置的元素组成,为
其分值为 |6 − 5 | + |5 − 6 | + | 7 − 5 | + |5 − 6 | + |6 − 7 |+ | 5 − 5 | + | 6 − 6 | = 6。
样例 2 说明
该矩阵中分值最小的 3 行 3 列的子矩阵由原矩阵的第 4 行、第 5 行、第 6 行与第 2 列、第 6 列、第 7 列交叉位置的元素组成, 选取的分值最小的子矩阵
解题思路:
蒟蒻我是去洛谷参考第2篇题解
详见代码
代码;
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,r,c,ans=2147483647,lc[20],hc[20][20],da[20][20],a[20][20],d[20];
void ycl()
{
for (int i=1;i<r;i++)
for (int j=1;j<=m;j++)
lc[j]=lc[j]+abs(a[d[i]][j]-a[d[i+1]][j]); //列差
for (int i=1;i<m;i++)
for (int j=i+1;j<=m;j++)
for (int k=1;k<=r;k++)
hc[i][j]=hc[i][j]+abs(a[d[k]][i]-a[d[k]][j]); //行差
}
void dp()
{
for(int i=1;i<=m;i++)
{
da[i][1]=lc[i];
for(int j=2;j<=c;j++)
for(int k=1;k<i;k++)
da[i][j]=min(da[i][j],da[k][j-1]+lc[i]+hc[k][i]);
if (da[i][c]<ans)
ans=da[i][c]; //前i列选了c行
}
}
void xh(int x)
{
if (x==r+1)
{
memset(hc,0,sizeof(hc));
memset(lc,0,sizeof(lc));
memset(da,0x7f,sizeof(da)); //赋初值
ycl(); //预处理行差,列差
dp();
return;
}
for (int i=d[x-1]+1;i<=n;i++)
{
d[x]=i;
xh(x+1);
d[x]=0;
}
}
int main()
{
freopen("submatrix.in","r",stdin);
freopen("submatrix.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&r,&c);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
xh(1); //选列
printf("%d",ans);
fclose(stdin);
fclose(stdout);
return 0;
}