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\);

  • 对于某些模板题,时间充裕时可以造个大样例,实在不行就检查一下。千万不要想当然的觉得自己是对的。

  • 简化题意不要漏掉关键信息。漏掉了估计离爆零就不远了

  • 日常炸裂。

posted @ 2021-01-03 20:14  Ti_Despairy  阅读(133)  评论(1)    收藏  举报