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}\)
思路
-
考場思路 看錯題目了.... 囧 ;
-
\(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\);
-
對於某些模板題,時間充裕時可以造個大樣例,實在不行就檢查一下。千萬不要想當然的覺得自己是對的。
-
簡化題意不要漏掉關鍵資訊。
漏掉了估計離爆零就不遠了。 -
日常炸裂。