2021 . 1 . 03 考试解题报告
得分 :
| 题目 | 期望得分 | 实际得分 | 主要原因 |
|---|---|---|---|
| \(A\) 数学题 | \(100\) | \(30\) | 看错题目 |
| \(B\) 网格 | \(100\) | \(20\) | \(BFS\) 未标记 |
| \(C\) 戈兰斜 | \(0\) | \(0\) | 不会 |
A 数学题
【题目描述】
给出一个 \(n\) 个未知数的方程,\(x_1\) , \(x_2\) , \(x_3\) \(\cdots\) \(x_n\) ;
求\(x_1 + x_2 + x_3 + \cdots + x_n == S\) 的正整数解的个数,
并且要保证,对于 \(\forall\) \(i\) , \(x_i\)与 \(x_{i+1}\)相差不大于$ P $;
思路
-
考场思路 看错题目了.... 囧 ;
-
\(90\) \(pts\)
考试后自己打的。
第一个数特判,从 \(1\) 到 \(S\) 枚举进行 \(DFS\)。
之后根据上一个数来判断下一个数,最后一个点 \(TLE\)。 -
正解
主体思想就是枚举每一个变量它的值时多少,然后判断相邻两个之间的差是否小于\(P\),和是否等于\(S\)。
其实因为我们是从前到后一个一个搜索变量的值的,所以,每一个数的范围已经由上一个数的值确定了。所以这样搜索的状态就比较少了。
【Code】
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<map>
#include<vector>
#include<set>
#define ull unsigned long long
#define ll long long
#define M 1000010
#define N 1010
#define INF 0x3f3f3f3f
using namespace std;
const int mod1 = 19260817;
const int mod2 = 19660813;
/*================================================*/
int n,s,p;
int vis[M];
int cnt;
/*================================================*/
inline int read()
{
int s = 0, f = 0;char ch = getchar();
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
void Dfs(int now,int last,int tot)//现在的数,总数,最小的数
{
if(now == n ){
if (abs(tot-last) <= p) cnt++;
return;
}
for(int i = max(last - p,1);i <= min(last + p,tot-(n - now));i ++) {
// vis[now] = i;
Dfs(now + 1,i,tot - i);
// vis[now] = 0;
}
}
/*=================================================*/
signed main()
{
// freopen("math.in","r",stdin);
// freopen("math.out","w",stdout);
scanf("%d%d%d",&n,&s,&p);
if(p == 0) {
if(s % n == 0) {//相差为零,都相等
printf("1");//不能整除
} else {
printf("0");//能整除
}
} else {
for(int i = 1;i <= s-(n-1);i ++) Dfs(2,i,s-i);
printf("%d",cnt);
}
return 0;
}
B 网格
【题目描述】
给出一个\(n * n\)的网格,有一些格子是障碍,再给出一对起点终点,求从起点到终点需要的最小步数,每次可以从一个格子走到上下左右 \(4\) 相邻的四个格子里。
思路
- 考场思路
和正解一样 - 正解
\(BFS\) 搜索一遍即可
【Code】
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stdlib.h>
#include<time.h>
#include<map>
#include<vector>
#include<set>
#define ull unsigned long long
#define ll long long
#define M 1000010
#define N 1010
#define qaq cout<<"可行QAQ"<<endl
#define INF 0x3f3f3f3f
using namespace std;
const int mod1 = 19260817;
const int mod2 = 19660813;
/*================================================*/
int n;
int mp[N][N];
int dx[5] = {0,1,-1,0,0};
int dy[5] = {0,0,0,1,-1};
bool vis[N][N];
int sx,sy,ex,ey;
struct node{
int x,y,cnt;
};
/*================================================*/
inline int read()
{
int s = 0, f = 0;char ch = getchar();
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
bool check(int x,int y)
{
if(x < 1 || x > n) return false;
if(y < 1 || y > n) return false;
return true;
}
int bfs()
{
queue<node> qp;
node now;
now.x = sx;
now.y = sy;
now.cnt = 0;
qp.push(now);//初始化
vis[sx][sy] = 1;
while(!qp.empty()) {
node cur;
cur = qp.front();
qp.pop();//记着清空
for(int k = 1;k <= 4;k ++) {
int fx = cur.x + dx[k];
int fy = cur.y + dy[k];
if(check(fx,fy)) {//判断是否出界
if(mp[fx][fy] != 1) {//有无障碍
if(!vis[fx][fy]){
vis[fx][fy] = 1;
int f_cnt = cur.cnt + 1;
if(fx == ex && fy == ey) return f_cnt;
qp.push((node){fx,fy,f_cnt});//扔进去在继续搜
}
}
}
}
}
return -1;
}
/*=================================================*/
signed main()
{
// freopen("grid.in","r",stdin);
// freopen("grid.out","w",stdout);
n = read();
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= n;j ++) {
mp[i][j] = read();
}
}
sx = read(); sy = read();
ex = read(); ey = read();
int ans = bfs();
printf("%d",ans);
return 0;
}
死因 :忘判断重复走到的点了。
if(!vis[fx][fy]) vis[fx][fy] = 1;
C 戈兰斜
【题目描述】
戈兰斜是一种在带数字的网格上玩的日本拼图游戏。目标是在网格的每个单元格中绘制对角线,连接到每个格点的对角线个数等于他对应的数字。另外,禁止对角线形成环。
第 一个图给出了游戏的初始状态。
第二个图给出了对应的一个解答。数据保证问题一定存在至少一解。
输入的第一 行包含一个的单个整数 \(n\) 表示棋盘的尺寸,棋盘是一个正方形。然后紧接 \(n+1\) 行。包含网格的初始状态。每行为一个含 \(n+1\) 个字符的字符串,字符要么为一个数字,要么为一个\(“·”\),其中数字都是 \(0\) 到 \(4\) 之间的任意整数,\(“·”\)表示连接到此格点的对角线数没有限制。
思路
- 考场思路
看(\(n<=7\)),想到了搜索。
\(DFS\) 搜索,先从有数字的地方开始搜,如果有个地方不能成立(斜杠重叠),就\(return\) 回去继续找,直到找到解为止,在看有 \(“·”\) 的地方,是否成立。
- 正解
我们从上到下,从左到右,一个格子一个格子搜索是放什么,但是这样可能过不去。
要加剪枝。首先要保证,不连成环,这个直接用一个支持删除的并查集就可以维护。
在格子中放斜杠时,注意判断上下左右的点度数的关系,遇到不合法的情况直接 \(return\) 就 \(ok\) 了。
【Code】
By : Ti_Despairy
Time : 2021,1,21;
思路 : 搜索
知识点 :搜索
*/
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stdlib.h>
#include<time.h>
#include<map>
#include<vector>
#include<set>
#define ull unsigned long long
#define ll long long
#define M 1000010
#define N 1010
#define qaq cout<<"可行QAQ"<<endl
#define INF 0x3f3f3f3f
using namespace std;
const int mod1 = 19260817;
const int mod2 = 19660813;
/*================================================*/
int dx[5] = {0, 0, 0, 1, 1};
int dy[5] = {0, 0, 1, 0, 1};
int n;
bool flag;
int ans[11][11];//储存答案
int fat[N];//并查集
int limit[11][11];//最多能连几条边 ,边界
int mp[11][11];//地图
int cur[11][11];//已经连了多少
/*================================================*/
inline int read()
{
int s = 0, f = 0;char ch = getchar();
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
bool check(int x,int y)
{
if(mp[x][y] == -1) return true;//如果为 - 1 ,则可以无限连,不用管他
if(cur[x][y] <= mp[x][y] && cur[x][y] + limit[x][y] >= mp[x][y]) return true;
//当已连的边小于 点数 && 已连的边 + 最多连的边 要 大于 要求连的边
//如果当前这个点连接的对角线还不到目标数目,那么继续;
//如果超过了可连接数limit,那么就停止;
return false;
}
int find(int x)//并查集,查找有无环路
{
if(!fat[x]) return x;
return find(fat[x]);
}
void dfs(int x,int y)
{
if(y == n) {//搜索完一行
y = 1; x += 1;
}
if(x == n) {//全搜索完,返回输出答案
flag = true;
return;
}
++cur[x][y];
++cur[x + 1][y + 1];//在 左上角 和 右下角 连边
--limit[x][y]; --limit[x + 1][y + 1];
--limit[x + 1][y]; --limit[x][y + 1];
//不能交叉 ,所以 四方向还可连的边 都减
bool vis = false;//看是否 超过
for(int i = 1;i <= 4;i ++) {
int fx = x + dx[i];
int fy = y + dy[i];
if(!check(fx,fy)) {
vis = true;
break;
}
}
int f1,f2;
if(!vis) {//若没超过
f1 = find((x - 1) * n + y);
f2 = find(x * n + y + 1);//是否存在环路
if(f1 != f2) {//不存在环路, 连边
ans[x][y] = 1;
fat[f1] = f2;
dfs(x , y + 1);
if(flag) return;//已找到答案
fat[f1] = 0; //回溯
}
}
// 以下同理
--cur[x][y];
--cur[x + 1][y + 1];
++cur[x + 1][y];
++cur[x][y + 1];
vis = false;
for(int i = 1;i <= 4;i ++) {
int fx = x + dx[i];
int fy = y + dy[i];
if(!check(fx,fy)) {
vis = true;
break;
}
}
if(!vis) {
f1 = find(x * n + y);
f2 = find((x - 1)* n + y + 1);
if(f1 != f2) {
ans[x][y] = 0;
fat[f1] = f2;
dfs(x,y + 1);
if(flag) return;
fat[f1] = 0;
}
}
--cur[x + 1][y];
--cur[x][y + 1];
++limit[x][y]; ++limit[x + 1][y + 1];//回溯
++limit[x + 1][y]; ++limit[x][y + 1];
}
/*=================================================*/
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int T;
scanf("%d",&T);
while(T--) {
flag = false;
memset(cur,0,sizeof(cur));
memset(fat,0,sizeof(fat));//多组数据记着清空
scanf("%d",&n); n += 1;
for(int i = 1;i <= n; i++ ) {
for(int j = 1;j <= n; j++) {
char x; cin >> x;
if(x == '.') mp[i][j] = -1;
else mp[i][j] = x - '0';//渡入时为字符
limit[i][j] = 4;
if((i == 1 || i == n) && (j == 1 || j == n)) {
limit[i][j] = 1;//判断为角时,只能连一条边
continue;
}
if(i == 1 || i == n || j == 1 || j == n) limit[i][j] = 2;
//判断为边时,连两条边
}
}
dfs(1,1);
for(int i = 1;i <= n - 1; i++) {
for(int j = 1;j <= n - 1; j++) {
if(!ans[i][j]) printf("/");
else printf("\\");
}
puts("");
}
}
return 0;
}
总结
-
注意审题 !!!!!
-
T1 把 \(x_i\) 与 \(x_{i+1}\) 之间相差不大于$ P $ 看成了最大值和最小值相差不大与 \(P\);
-
对于某些模板题,时间充裕时可以造个大样例,实在不行就检查一下。千万不要想当然的觉得自己是对的。
-
简化题意不要漏掉关键信息。
漏掉了估计离爆零就不远了。 -
日常炸裂。

浙公网安备 33010602011771号