第2課 これは本です

<script type="text/javascript"><!-- google_ad_client = "pub-0241434510974184"; /* 博客文章广告728x90, */ google_ad_slot = "7316585398"; google_ad_width = 728; google_ad_height = 90; // --></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>

基本本文:

1.これは本です。

2.それは何ですか。

3.あれはだれの傘ですか。

4.このカメラはスミスさんのです。

甲:これはテレビですか。

乙:いいえ,それはテレビではありません。パソコンです。

甲:それは何ですか。

乙:これは日本語の本です。

甲:森さんのかばんはどれですか。

乙:あのかばんです。

甲:そのノートはだれのですか。

乙:わたしのです。

家族の写真

小野:李さん、それは何ですか。

李:これですか。家族の写真です。

小野:この方はどなたですか。

李:わたしの母です。

小野:お母さんはおいくつですか。

李:52に歳です。

李:小野さん、これ、どうぞ。

小野:えっ、何ですか。

李:お土産です。

小野:わあ、シルクのハンカチですか。

李:ええ。スウトウのハンカチです。中国の名産品です。

小野:どうもありがとうございます。

c++:# U577052 阿伯次B ## 题目背景 **翻译仅供参考** 给定一个正整数 $N$ 和一个 $N \times N$ 的矩阵。 任意选定一个位置,再从 8 个方向(上下左右和四个斜的方向)选择一个固定的方向连续移动 $N−1$ 次,使经过的数字拼凑起来最大。输出最大可能得到的数字。 这个矩阵是上下左右联通的,例如: $(i,N)$ 是在 $(i,1)$ 的左边,$(N,i)$ 是在 $(1,i)$ 的上面。 $(1 \le i \le N)$ ## 题目描述 正整数 $ N $ が与えられます。 $ N $ 行 $ N $ 列のマス目があり、上から $ i $ 行目、左から $ j $ 列目のマスには数字 $ A_{i,j} $ が書かれています。 このマス目は上下および左右がつながっているものとします。つまり以下が全て成り立ちます。 - $ (1,i) $ の上のマスは $ (N,i) $ であり、$ (N,i) $ の下のマスは $ (1,i) $ である。$ (1\le\ i\le\ N) $ - $ (i,1) $ の左のマスは $ (i,N) $ であり、$ (i,N) $ の右のマスは $ (i,1) $ である。$ (1\le\ i\le\ N) $ 高橋君は、上下左右および斜めの $ 8 $ 方向のうちいずれかを初めに選びます。そして、好きなマスから決めた方向に $ 1 $ マス移動することを $ N-1 $ 回繰り返します。 高橋君は $ N $ 個のマス上を移動することになりますが、高橋君が通ったマスに書かれている数字を左から通った順番に並べた整数としてあり得る最大のものを求めてください。 ## 输入格式 入力は以下の形式で標準入力から与えられる。 > $ N $ $ A_{1,1}A_{1,2}\dots\ A_{1,N} $ $ A_{2,1}A_{2,2}\dots\ A_{2,N} $ $ \vdots $ $ A_{N,1}A_{N,2}\dots\ A_{N,N} $ ## 输出格式 答えを出力せよ。 ## 输入输出样例 #1 ### 输入 #1 ``` 4 1161 1119 7111 1811 ``` ### 输出 #1 ``` 9786 ``` ## 输入输出样例 #2 ### 输入 #2 ``` 10 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 ``` ### 输出 #2 ``` 1111111111 ``` ## 说明/提示 ### 制約 - $ 1\ \le\ N\ \le\ 10 $ - $ 1\ \le\ A_{i,j}\ \le\ 9 $ - 入力はすべて整数。 ### Sample Explanation 1 高橋君が上から $ 2 $ 行目、左から $ 4 $ 列目のマスから出発し、右下に進むことで、通ったマスに書かれた数字を並べ $ 9786 $ を作ることができます。 $ 9786 $ より大きい値を作ることはできないため、$ 9786 $ が解です。 ### Sample Explanation 2 32bit整数型に答えが収まるとは限らないことに注意してください。
最新发布
07-08
<think>题目要求:给定一个N×N的矩阵,矩阵是循环的(即上下左右边界相连)。从任意位置开始,选择一个方向(8个方向之一),连续移动N-1次(共经过N个格子),将经过的数字按顺序拼接成一个整数,求能得到的最大整数。 注意:N最大为10,所以我们可以枚举所有可能的起点和所有8个方向,然后生成对应的数字,并取最大值。 步骤: 1. 读取输入:第一行是N,接下来N行,每行是一个长度为N的字符串,表示矩阵的一行。 2. 定义8个移动方向的方向数组,包括上下左右和四个斜方向。例如: - 上: (-1, 0) - 下: (1, 0) - 左: (0, -1) - 右: (0, 1) - 左上: (-1, -1) - 右上: (-1, 1) - 左下: (1, -1) - 右下: (1, 1) 3. 由于矩阵是循环的,所以当坐标超出边界时,需要取模(或通过加减N来调整到有效范围内)。具体来说,对于行r和列c,新的位置为(r + dr, c + dc),然后对行和列分别取模N(注意:当位置为负数时,通过加N再取模来调整)。 4. 枚举每个起点(i, j)和每个方向(d),从起点开始沿着方向d移动N-1步,得到N个位置上的数字,拼接成一个整数。 5. 在所有的整数中取最大值。 6. 输出最大值。 注意:因为N最大为10,所以最多有10*10*8=800种路径,每种路径生成一个长度为N的整数(最大10位),完全在可处理范围内。 实现细节: - 方向数组:使用8个方向的(dr, dc)组合。 - 循环处理:对于每个起点和方向,我们模拟移动N-1步(加上起点一共N步),每一步更新当前位置(注意循环边界)。 - 拼接数字:我们可以用一个字符串来收集每一步的数字,最后将这个字符串转换成整数,或者直接比较字符串(因为数字位数相同,可以按字典序比较,但注意这里所有数字都是1-9,没有前导0问题,所以可以直接比较字符串,但转换成整数比较也可以,因为N最大10,整数在10^10以内,可以用long long存储)。 但是注意:题目要求输出的是整数,但样例2输出的是"1111111111",所以我们需要输出整数,但注意可能很大,所以用字符串比较然后最后输出字符串?或者用大整数?但题目说明32位整数不一定存得下,所以我们需要用字符串或者用64位整数(10位数最大是9999999999,小于2^34,所以64位整数可以存下)。因此,我们可以用long long(64位)来存储和比较。 但是,题目中数字范围是1-9,没有0,所以不会出现前导0,但拼接的数字可能很大,用整数比较时,由于N最大10,所以最大数字是10^10-1,在64位整数范围内(最大约9e18),所以可以用long long存储。 步骤: 1. 读取输入,将矩阵存储为一个二维字符数组,或者二维整数数组?因为每个位置是一个字符,所以我们可以用字符数组,然后拼接字符串,再转换成整数。或者直接按位构造整数。 但是,我们也可以不转换成整数,而是直接比较字符串?因为数字的位数相同,所以字符串的字典序大小就是数值大小(因为位数相同)。这样更安全,因为不会溢出(虽然本题不会溢出,但用字符串比较可以避免转换,且更通用)。 然而,题目要求输出整数,所以我们最后需要输出一个整数。但我们可以用字符串来表示路径上的数字,然后比较这些字符串(因为位数相同,所以字符串的字典序就是数值大小),然后取最大的字符串,最后输出这个字符串即可(因为题目要求输出整数,而样例输出也是直接输出数字字符串)。 所以,我们可以: - 对于每条路径,生成一个长度为N的字符串(按顺序记录每个位置的字符)。 - 然后维护一个最大字符串(初始为空),然后和当前生成的字符串比较(按字典序,因为位数相同,所以字典序大的就是数值大)。 - 最后输出这个最大字符串。 注意:字符串比较时,如果两个字符串长度相同,那么字典序大的数值大,所以我们可以直接用字符串的字典序比较。 实现: 1. 读取输入。 2. 初始化一个空字符串ans,用于记录最大字符串。 3. 定义方向数组dirs,包含8个方向的(dr,dc)。 4. 双重循环枚举起点(i, j)(0<=i,j<N)。 5. 对于每个起点,枚举8个方向。 6. 从起点(i,j)开始,按照当前方向移动N-1步,每一步收集当前位置的字符,并将位置更新为下一个位置(注意循环边界:使用模运算,但注意负数的处理:我们可以用 (r%N + N) % N 来确保非负,或者当r为负时,r+N再模N)。 7. 将收集到的N个字符组成一个字符串s。 8. 如果s大于ans(用字符串比较),则更新ans为s。 9. 循环结束后,输出ans。 边界处理:对于位置(i, j),下一步位置为 (i+dr, j+dc),然后调整: new_i = (i + dr + N) % N // 加N是为了避免负数,然后模N得到0到N-1 new_j = (j + dc + N) % N 但是注意:当dr为正时,加N模N不会改变结果(因为正数模N不变,但负数需要加N再模N)。所以统一用 (x+N) % N 可以处理负数情况,因为当x为负时,加N变成正数,然后模N。当x在[0, N-1]时,加N模N等于x模N等于x(因为x<N)。所以这样处理是合理的。 但是,如果dr是0,那么(i+0)在[0, N-1]内,所以模N后还是i。同样,如果dr=1,那么i+1如果等于N,那么(N) % N = 0,而实际上我们希望它变成0(因为矩阵是循环的,第N行的下一行是第一行)。所以这样处理正确。 所以,每一步的位置更新为: ni = (ni + dr + N) % N // 初始ni为起点i,然后每一步加上dr nj = (nj + dc + N) % N 但是,注意:我们只需要在每一步更新位置时,用模运算来确保位置在[0, N-1]内。 然而,我们也可以这样:从起点开始,走k步(0<=k<=N-1),位置为: r = (i + k*dr + N*100) % N // 加一个大数确保非负,然后模N。但加N*100没有必要,因为k最大为9(N<=10),所以k*dr最大绝对值为9*1=9,所以加N(10)就足够变成正数了。因此,我们可以每一步这样计算: r = (i + k*dr) % N 但是,如果(i+k*dr)是负数,模N可能是负数?所以为了确保非负,我们可以: r = ( (i + k*dr) % N + N ) % N 或者: r = (i + k*dr) % N; if (r < 0) r += N; 然后r %= N; // 但这样可能多次模,其实加一次N然后模一次即可。 我们可以在每一步计算位置时,用: int ni = (i + k * dr + N * 10) % N; // 加10倍N确保非负,但模N后等价于加0模N,所以不行。应该用: int ni = (i + k * dr) % N; if (ni < 0) ni += N; ni %= N; // 但这样不对,因为如果ni为负,加上N后可能还是负?所以应该用: ni = ( (i + k * dr) % N + N ) % N; 但是,模运算的性质:我们希望将坐标映射到[0, N-1]。 ni = (i + k*dr) % N; 这个表达式可能得到负数(例如,i=0, dr=-1, k=1,则-1 % N 在C++中是负数)。所以我们可以用: ni = ( (i + k*dr) % N + N ) % N; 但是,这样写:先模N,然后加上N,再模N,可以确保非负。因为模N后范围在[-N+1, N-1]之间,加上N后范围在[1, 2N-1]之间,再模N就得到[0, N-1]。 但是,还有一种更直接的方法:每一步计算新位置时,用: ni = (i + k*dr) % N; if (ni < 0) ni += N; ni %= N; // 这里需要再模一次吗?如果ni是负数,加上N后可能超过N?所以需要模一次。但如果我们加的是N,那么ni在[0, 2N-1]之间,所以模N即可。 实际上,我们可以这样: ni = ( (i + k*dr) % N + N ) % N; 或者,因为k*dr的最大绝对值不超过9(k<=9, |dr|<=1),所以i+k*dr最小为0-9=-9,最大为9+9=18(当N=10时)。所以我们可以: ni = (i + k*dr) % N; if (ni < 0) ni += N; // 调整到[0, N-1] if (ni >= N) ni -= N; // 但这样不对,因为模N后正数不会超过2N-1,所以只需要调整负数。 实际上,模运算后,正数部分在[0, N-1]内,负数部分在[-N+1, -1]内。所以只需要调整负数:加上N即可。 所以: ni = (i + k*dr) % N; if (ni < 0) ni += N; 然后ni一定在[0, N-1]吗?不一定,因为如果(i+k*dr)是正数且大于N,那么模N后可能为正数,但小于N?不对,模N后范围是[-N+1, N-1]。所以对于正数模N后不会超过N-1(非负数部分在[0, N-1]),负数部分在[-N+1, -1],所以加上N后变成[1, N-1](都是正数且小于2N-1,但不会超过N?因为最大负数加N后最大为N-1)。所以这样调整后,ni在[0, N-1]内?不对,因为正数部分模N后已经是[0, N-1],负数部分调整后是[1, N-1](没有0?)。我们需要0。 例子:i=0, dr=-1, k=1: (0-1)%10 = -1 %10,在C++中为-1,然后ni=-1<0,所以ni=-1+10=9,正确(循环到上一行最后一行)。 所以,调整负数的步骤后,正数部分不需要调整(因为模N后正数在[0,N-1])。所以我们可以: ni = (i + k*dr) % N; if (ni < 0) ni += N; 但是,这样写,当(i+k*dr)为正数时,模N后可能为正数也可能为负数?实际上,在C++中,负数的模运算结果是负数。所以我们需要调整所有负数。但正数模N后如果小于N,则不变,如果大于等于N,模N后得到0到N-1,所以正数部分不需要调整。所以只需要调整负数。 但是,我们也可以避免模运算的负数问题,用数学模: ni = ( (i + k*dr) % N + N ) % N; 这个公式可以确保结果在[0, N-1]内。 所以,我们选择用这个公式。 同样处理列坐标。 但是,注意:我们每一步的位置是起点(i,j)加上k倍的步长(k=0,1,...,N-1),所以我们可以预先计算每一步的位置,然后收集字符。 具体代码步骤: 1. 读入N,然后读入N行字符串,存入一个vector<string> grid。 2. 定义方向向量:vector<pair<int,int>> dirs = {{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}}; // 8个方向:上、右上、右、右下、下、左下、左、左上 3. 初始化一个空字符串ans(初始化为空字符串,或者初始化为由N个'0'组成的字符串?但题目中数字最小是1,所以我们可以初始化为由N个'0'组成的字符串,但这样不好,因为实际数字没有0。我们可以初始化为第一个起点和第一个方向生成的字符串,然后更新。但更简单的是初始化为空字符串,然后在比较时,如果ans为空,则直接赋值。或者初始化为全0字符串,但题目要求最大整数,所以我们可以初始化为一个很小的值,比如全0字符串(但实际数字最小是1,所以全0字符串不会出现)。但为了安全,我们可以取第一个生成的字符串作为初始值。 4. 枚举起点(i,j)和方向d: for i in [0, N) for j in [0, N) for each direction in dirs: string s = ""; for k in [0, N-1]: // 计算第k步的位置(包括起点,k=0就是起点,k=1是第一步移动后,...,k=N-1是第N-1步移动后) int ni = (i + k * dr + N * 100) % N; // 这样不行,因为模之前加一个大数模N后还是等价于模N。 正确做法: int ni = ( (i + k * dirs[d].first) % N + N ) % N; int nj = ( (j + k * dirs[d].second) % N + N ) % N; s += grid[ni][nj]; // 此时s是一个由N个字符组成的字符串 if (ans == "" || s > ans) ans = s; 5. 输出ans。 注意:字符串比较运算符在C++中可以直接用>,比较的是字典序,由于位数相同,所以字典序大的数值大。 但是,我们也可以一开始就把ans初始化为由N个'0'组成的字符串,因为题目中数字最小是1,所以任何路径生成的字符串都会大于这个字符串(因为第一位至少是1)。所以我们可以: string ans = string(N, '0'); // 初始化为N个'0' 然后更新。 但是,如果N=0?题目N>=1,所以没问题。 代码实现: 注意:输入格式:第一行是N,然后N行,每行一个长度为N的字符串。 例如样例1: 4 1161 1119 7111 1811 我们存储grid[0]="1161", grid[1]="1119", grid[2]="7111", grid[3]="1811"。 然后枚举起点和方向。 样例1的输出是9786,说明存在一条路径生成"9786"。 根据题目描述:从第2行第4列(即索引为1行3列,因为行和列从0开始)出发,方向是右下(即dr=1, dc=1)。那么: 起点:grid[1][3] = '9' 第一步:位置(1+1, 3+1) = (2,4) -> 调整列:4%4=0,所以位置(2,0) -> grid[2][0]='7' 第二步:位置(2+1,0+1)=(3,1) -> grid[3][1]='8' 第三步:位置(3+1,1+1)=(4,2) -> 调整行:4%4=0,所以位置(0,2) -> grid[0][2]='6' 所以字符串为"9786",正确。 所以算法正确。 代码实现: 我们使用C++,注意输入输出。 注意:方向数组的8个方向,我们按照8个方向依次枚举。 代码: ```cpp #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; int main() { int N; cin >> N; vector<string> grid(N); for (int i = 0; i < N; i++) { cin >> grid[i]; } // 8个方向:上、右上、右、右下、下、左下、左、左上 vector<pair<int, int>> dirs = {{-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}}; string ans = string(N, '0'); // 初始化为N个'0' // 枚举每个起点 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { // 枚举每个方向 for (auto& dir : dirs) { int dr = dir.first; int dc = dir.second; string s = ""; // 从起点开始,走0到N-1步 for (int k = 0; k < N; k++) { // 计算当前位置 int ni = ( (i + k * dr) % N + N ) % N; // 加N再模N确保非负 int nj = ( (j + k * dc) % N + N ) % N; s += grid[ni][nj]; } // 更新答案 if (s > ans) { ans = s; } } } } cout << ans << endl; return 0; } ``` 测试样例1: 输入: 4 1161 1119 7111 1811 输出:9786 测试样例2: 输入: 10 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 1111111111 输出:1111111111 所以代码正确。 注意:在计算位置时,我们使用了双重模运算:先模N,再加N,再模N。这样确保位置在[0, N-1]内。 由于N最大为10,所以循环次数为10*10*8*10=8000次,非常快。 因此,按照上述代码即可。</think>### 问题解答 题目要求在一个 $N \times N$ 的循环矩阵中,从任意起点出发,选择一个方向(8个方向之一)连续移动 $N-1$ 次,将经过的数字拼接成最大整数。循环矩阵的边界是相连的(例如,从最右列向右移动会到达最左列)。 #### 解决思路 1. **枚举所有可能性**:由于 $N \leq 10$,可以枚举每个起点 $(i, j)$ 和8个移动方向(上、下、左、右、左上、右上、左下、右下)。 2. **模拟移动路径**:对于每个起点和方向,计算移动 $N-1$ 次后的路径,收集经过的 $N$ 个数字组成字符串。 3. **处理循环边界**:使用模运算确保移动时超出边界后循环到另一侧: - 新行索引:`ni = ((i + k * dr) % N + N) % N` - 新列索引:`nj = ((j + k * dc) % N + N) % N` 4. **比较并更新最大值**:将生成的字符串与当前最大值比较(位数相同,字典序即数值大小),保留最大字符串。 5. **输出结果**:所有路径中的最大数字字符串。 #### 示例代码(C++) ```cpp #include <iostream> #include <vector> #include <string> using namespace std; int main() { int N; cin >> N; vector<string> grid(N); for (int i = 0; i < N; i++) { cin >> grid[i]; } // 8个方向: {dr, dc} = 上, 右上, 右, 右下, 下, 左下, 左, 左上 vector<pair<int, int>> dirs = {{-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}}; string ans = string(N, '0'); // 初始化为最小可能值 (N个'0') for (int i = 0; i < N; i++) { // 枚举起点行 for (int j = 0; j < N; j++) { // 枚举起点列 for (auto& dir : dirs) { // 枚举8个方向 int dr = dir.first; int dc = dir.second; string s = ""; for (int k = 0; k < N; k++) { // 移动N-1次,共N个点 int ni = ((i + k * dr) % N + N) % N; // 循环边界处理 int nj = ((j + k * dc) % N + N) % N; s += grid[ni][nj]; } if (s > ans) ans = s; // 更新最大字符串 } } } cout << ans << endl; return 0; } ``` #### 代码说明 - **输入处理**:读入 $N$ 和矩阵,存储为字符串数组 `grid`。 - **方向定义**:`dirs` 包含8个方向的移动增量 $(dr, dc)$。 - **路径模拟**:对每个起点和方向,生成路径字符串: - 使用模运算处理循环边界:`((...)%N + N)%N` 确保索引非负且合法。 - **最大值更新**:通过字符串比较(字典序)更新最大值 `ans`。 - **输出**:最终的最大数字字符串。 #### 样例验证 - **输入1**:`N=4`,矩阵为: ``` 1161 1119 7111 1811 ``` **输出**:`9786`(从第2行第4列出发,右下方向:9→7→8→6)。 - **输入2**:`N=10`,全1矩阵。 **输出**:`1111111111`(所有路径结果相同)。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值