神奇的风 (Standard IO)

本文介绍了一种基于特定地形和能量规则的风传播模型,通过搜索算法确定从唯一风口出发的风如何传播到目的地,考虑了不同地形对风的影响及风能叠加规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意/Description:

       T博士最近在研究一种神奇的风。
  这种风有一个源头,称之为“风口”。风口的风要么是顺时针的,要么是逆时针的。
  这种风是有能量的。
  这种风是能传播的,如果风口周围都能传播风,风会像这样产生下一级的风:


       (箭头表示风的转向,最中间的是风口,图中风口的风是顺时针的,对于风口的风是逆时针的情形可通过物理模型想象一下或由这个图推理。)
  产生的下一级风的能量依据新产生的风所在的地形决定,有两种可以传递这种风的地形:
  一、平地,下一级风的能量与风口的风能量相等,这种地形用“.”表示。
  二、少量障碍物,下一级风的能量是风口的风能量的一半,这种地形用“*”表示。
  每个新产生的风都能作为新的风口继续产生下一级风。
  此外,还有一种地形是不能传递风的:大量障碍物,用“#”表示。
  如果在风口的某一方向是“#”地形,则该方向不会产生下一级风,其他方向视该位置的地形而定,就是说,如果是“.”或“*”地形则该方向照样传播。
  T博士还发现了这种神奇的风的叠加规则:两股风叠加时,无论它们的转向是否相同,都只保留能量较大的那股风,即风叠加后的能量和转向都与叠加前能量较大的那股风相同。而如果叠加的两股风的能量相同且转向相同,则保留其中任意一股。两股风能量相同且转向不同时叠加的情况未知,因为T博士至今还没碰到这样的情况。
  T博士画了几张地图,他想知道地图上的某点风的转向和能量。


读入/Input

       输入文件wind.in的第一行是两个整数m和n,表示地图大小为m行n列。
  接下来的m行,每行是n个字符,只会是以下的几种:
  “S”表示风口,保证图中有且仅有一个“S”。
  “E”表示目的地,即T博士想知道的那个点。
  “.”“*”“#”见描述。
  第m+2行是一个整数“0”或“1”,分别表示风口的风是顺时针方向或逆时针方向。
  约定“S”处的风能量为16384,“S”和“E”处的地形均为平地。


输出/Output

       输出文件为wind.out。
  若目的地会有风传到,则输出两行:第一行为一个整数,为目的地的风的能量;第二行为一个整数“0”或“1”,表示意义同输入格式。此种情形下保证目的地的风的能量大于0。
  若目的地没有风传到,则输出一行:“There’s no wind!”(双引号内的字符,全英文标点)。


题解/solution

       题目很长,但很容易想到搜索。总之,搜就行了。


代码/Code

const
  dx:array [1..4] of integer=(1,-1,0,0);
  dy:array [1..4] of integer=(0,0,1,-1);

type
  arr=record
    x,y:longint;
  end;
var
  n,m,x1,x2,y1,y2:longint;
  e:array [0..500001] of arr;
  a:array [0..201,0..201] of char;
  b,v:array [0..201,0..201] of boolean;
  f:array [0..201,0..201] of longint;
procedure init;
var
  i,j,t:longint;
begin
  readln(n,m);
  for i:=1 to n do
    begin
      for j:=1 to m do
        begin
          read(a[i,j]);
          if a[i,j]='S' then
            begin
              x1:=i; y1:=j;
              a[i,j]:='.';
            end;
          if a[i,j]='E' then
            begin
              x2:=i; y2:=j;
              a[i,j]:='.';
            end;
          if a[i,j]='#' then
            v[i,j]:=true;
        end;
      readln;
    end;
  for i:=0 to n+1 do
    begin
      v[i,0]:=true; v[i,m+1]:=true;
    end;
  for i:=0 to m+1 do
    begin
      v[0,i]:=true; v[n+1,i]:=true;
    end;
  readln(t);
  if t=0 then b[x1,y1]:=false
         else b[x1,y1]:=true;
  f[x1,y1]:=16384;
end;

function fd(x,y:longint):boolean;
begin
  exit((x>=1) and (x<=n) and (y>=1) and (y<=m));
end;

procedure main(x,y:longint);
var
  i,l,r,tx,ty,tdx,tdy:longint;
begin
  l:=1; r:=1;
  e[1].x:=x; e[1].y:=y;
  v[x,y]:=true;
  while l<=r do
    begin
      tx:=e[l].x; ty:=e[l].y;
      for i:=1 to 4 do
        begin
          tdx:=tx+dx[i];
          tdy:=ty+dy[i];
          if (not fd(tdx,tdy)) or (a[tdx,tdy]='#') then continue;
          if a[tdx,tdy]='.'  then
            begin
              if f[tx,ty]>f[tdx,tdy] then
                begin
                  f[tdx,tdy]:=f[tx][ty];
                  if not b[tx,ty] then b[tdx,tdy]:=true
                                  else b[tdx,tdy]:=false;
                  if not v[tdx,tdy] then
                    begin
                      inc(r);
                      e[r].x:=tdx;
                      e[r].y:=tdy;
                      v[tdx,tdy]:=true;
                    end;
                end;
            end else
            begin
              if a[tdx,tdy]='*' then
                begin
                  if f[tx,ty] div 2>f[tdx,tdy] then
                    begin
                      f[tdx,tdy]:=f[tx,ty] div 2;
                      if not b[tx,ty] then b[tdx,tdy]:=true
                                      else b[tdx,tdy]:=false;
                      if not v[tdx,tdy] then
                        begin
                          inc(r);
                          e[r].x:=tdx;
                          e[r].y:=tdy;
                          v[tdx,tdy]:=true;
                        end;
                    end;
                end;
            end;
        end;
      inc(l);
      v[tx,ty]:=false;
    end;
end;

begin
  fillchar(f,sizeof(f),0);
  init;
  main(x1,y1);
  if f[x2,y2]<>0 then
    begin
      writeln(f[x2,y2]);
      if b[x2,y2] then write('1')
                  else write('0');
    end else write('There','''','s no wind!');
end.


<think>在Linux系统中,文件I/O操作是程序与文件系统交互的基础。C++提供了多种方式进行文件I/O操作,主要包括: 1. 使用C语言的文件I/O函数(如fopen, fread, fwrite, fclose等) 2. 使用C++的流库(如ifstream, ofstream, fstream等) 3. 使用Linux系统调用(如open, read, write, close等) 下面我们将分别介绍这三种方式,并给出示例代码。 一、使用C语言的文件I/O函数 这些函数在<stdio.h>中声明,是标准C库的一部分,在C++中通常包含<cstdio>。 示例:使用C库函数进行文件读写 ```cpp #include <cstdio> #include <cstring> int main() { FILE *file = fopen("test.txt", "w"); // 打开文件用于写入 if (file == nullptr) { perror("Failed to open file"); return 1; } const char *text = "Hello, World!"; size_t bytes_written = fwrite(text, 1, strlen(text), file); if (bytes_written < strlen(text)) { perror("Failed to write to file"); } fclose(file); // 关闭文件 // 重新打开文件用于读取 file = fopen("test.txt", "r"); if (file == nullptr) { perror("Failed to open file"); return 1; } char buffer[50]; size_t bytes_read = fread(buffer, 1, sizeof(buffer), file); if (bytes_read > 0) { buffer[bytes_read] = '\0'; // 添加字符串结束符 printf("Read from file: %s\n", buffer); } else { perror("Failed to read from file"); } fclose(file); return 0; } ``` 二、使用C++流库 C++标准库提供了<fstream>头文件,其中包含ifstream(输入文件流)、ofstream(输出文件流)和fstream(输入输出文件流)。 示例:使用C++流进行文件读写 ```cpp #include <fstream> #include <iostream> #include <string> int main() { // 写入文件 std::ofstream out_file("test.txt"); if (!out_file.is_open()) { std::cerr << "Failed to open file for writing." << std::endl; return 1; } out_file << "Hello, World with C++ streams!" << std::endl; out_file.close(); // 读取文件 std::ifstream in_file("test.txt"); if (!in_file.is_open()) { std::cerr << "Failed to open file for reading." << std::endl; return 1; } std::string line; while (std::getline(in_file, line)) { std::cout << "Read from file: " << line << std::endl; } in_file.close(); return 0; } ``` 三、使用Linux系统调用 系统调用是操作系统内核提供的接口,在C++中可以直接调用。文件操作的系统调用包括:open, read, write, close等。需要包含头文件<fcntl.h>、<unistd.h>、<sys/stat.h>等。 示例:使用系统调用进行文件读写 ```cpp #include <fcntl.h> #include <unistd.h> #include <cstring> #include <iostream> #include <cerrno> int main() { // 打开文件用于写入,如果不存在则创建,权限为0644 int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { std::cerr << "Failed to open file for writing: " << strerror(errno) << std::endl; return 1; } const char *text = "Hello, World with system calls!"; ssize_t bytes_written = write(fd, text, strlen(text)); if (bytes_written == -1) { std::cerr << "Failed to write to file: " << strerror(errno) << std::endl; } close(fd); // 重新打开文件用于读取 fd = open("test.txt", O_RDONLY); if (fd == -1) { std::cerr << "Failed to open file for reading: " << strerror(errno) << std::endl; return 1; } char buffer[50]; ssize_t bytes_read = read(fd, buffer, sizeof(buffer)-1); // 留一个位置给'\0' if (bytes_read == -1) { std::cerr << "Failed to read from file: " << strerror(errno) << std::endl; } else { buffer[bytes_read] = '\0'; std::cout << "Read from file: " << buffer << std::endl; } close(fd); return 0; } ``` 解释: 1. C库函数:跨平台性好,使用方便,有缓冲机制,效率较高。 2. C++流:面向对象,类型安全,支持运算符重载(<<和>>),同样有缓冲机制,但可能比C库函数稍慢。 3. 系统调用:最底层,没有缓冲(除非自己设置),效率高,但跨平台性差,且需要处理更多细节(如错误码errno)。 在选择时,如果考虑跨平台,建议使用C库函数或C++流;如果追求高性能或需要更精细的控制(如非阻塞I/O、文件锁等),则可以使用系统调用。 注意:使用系统调用时,错误处理非常重要,因为系统调用失败时会返回-1,并设置errno,通过strerror(errno)可以获取错误信息字符串。 相关问题: 1. 文件I/O操作中的缓冲机制是什么?为什么需要缓冲? 2. 在C++中,如何同时读写同一个文件(即如何实现文件的随机访问)? 3. 使用系统调用进行文件操作时,如何设置文件偏移量(即如何实现随机访问)? 4. 什么是非阻塞I/O?在Linux中如何实现非阻塞文件I/O?</think>### Linux 文件 I/O 操作详解 在 Linux 中,C++ 文件 I/O 操作主要通过以下三种方式实现: 1. **C 标准库函数**(`<cstdio>`) 2. **C++ 流库**(`<fstream>`) 3. **Linux 系统调用**(`<unistd.h>` 和 `<fcntl.h>`) #### 1. 使用 C 标准库函数(跨平台) ```cpp #include <cstdio> int main() { // 写入文件 FILE* file = fopen("example.txt", "w"); if (!file) { perror("fopen failed"); return 1; } fprintf(file, "Hello C Standard I/O!\n"); fclose(file); // 读取文件 file = fopen("example.txt", "r"); char buffer[100]; while (fgets(buffer, sizeof(buffer), file)) { printf("Read: %s", buffer); } fclose(file); return 0; } ``` **特点**: - 带缓冲区,效率较高 - 跨平台兼容性好 - 使用 `fopen()`/`fclose()` 管理文件流 #### 2. 使用 C++ 流库(面向对象) ```cpp #include <fstream> #include <iostream> int main() { // 写入文件 std::ofstream out("cpp_example.txt"); if (!out) { std::cerr << "File open error!" << std::endl; return 1; } out << "Hello C++ fstream!\n"; out.close(); // 读取文件 std::ifstream in("cpp_example.txt"); std::string line; while (std::getline(in, line)) { std::cout << "Read: " << line << std::endl; } in.close(); return 0; } ``` **特点**: - 类型安全(支持运算符重载 `<<`/`>>`) - 异常处理支持(可通过 `exceptions()` 启用) - RAII 格资源管理 #### 3. 使用 Linux 系统调用(底层控制) ```cpp #include <fcntl.h> #include <unistd.h> #include <cstring> #include <cerrno> int main() { // 写入文件 (O_CREAT | O_WRONLY | O_TRUNC) int fd = open("syscall_example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { perror("open failed"); return 1; } const char* text = "Hello Linux syscalls!\n"; write(fd, text, strlen(text)); close(fd); // 读取文件 fd = open("syscall_example.txt", O_RDONLY); char buf[100]; ssize_t bytes_read = read(fd, buf, sizeof(buf) - 1); if (bytes_read > 0) { buf[bytes_read] = '\0'; printf("Read: %s", buf); } close(fd); return 0; } ``` **特点**: - 无缓冲 I/O(直接内核交互) - 精细控制(文件描述符、文件状态标志) - 支持高级功能(`fcntl()`, `ioctl()`) ### 关键概念解析 1. **文件描述符 (File Descriptor)**: - 系统调用使用的整数标识符(0=stdin, 1=stdout, 2=stderr) - 通过 `open()` 创建,`close()` 释放 2. **打开模式标志**: - `O_RDONLY`:只读 - `O_WRONLY`:只写 - `O_RDWR`:读写 - `O_CREAT`:不存在时创建 - `O_TRUNC`:清空现有内容 - `O_APPEND`:追加写入 3. **错误处理**: - 系统调用返回 -1 时设置 `errno` - 使用 `perror()` 或 `strerror(errno)` 获取错误信息 ### 性能对比 | 方法 | 缓冲机制 | 执行效率 | 控制粒度 | 跨平台性 | |---------------|------------|---------|---------|---------| | C 标准库 | 全缓冲 | 中高 | 中 | 优秀 | | C++ 流库 | 全缓冲 | 中 | 高 | 优秀 | | 系统调用 | 无缓冲 | 最高 | 最高 | Linux 专用 | **最佳实践**: - 常规文本处理:C++ 流库(类型安全) - 高性能二进制 I/O:C 标准库(`fread`/`fwrite`) - 设备级操作:系统调用(`ioctl` 等特殊控制)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值