1. 程式人生 > 實用技巧 >2021 . 1 . 03 考試解題報告

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

  • 對於某些模板題,時間充裕時可以造個大樣例,實在不行就檢查一下。千萬不要想當然的覺得自己是對的。

  • 簡化題意不要漏掉關鍵資訊。漏掉了估計離爆零就不遠了

  • 日常炸裂。