棋盤 K皇后

愚末語發表於2020-11-16

棋盤 K皇后

洛谷P2105 作為我在洛谷AC的第100題,拍照留念。
我不會告訴你我做這道題用了9個小時的,哈哈
題目描述:

小 Z 最近撿到了一個棋盤,他想在棋盤上擺放 KK 個皇后。他想知道在他擺完這 KK 個皇后之後,棋盤上還有多少個格子是不會被攻擊到的。
注意:一個皇后會攻擊到這個皇后所在的那一行,那一列,以及兩條對角線。

初解此題,我的思路侷限於通過一個點的位置來找到皇后能走的位置,再用總的格子去減皇后能走的格子。
這樣想似乎很合理,如果題目的資料控制在小範圍,我一定會這樣做。但是,很不幸的是他給的資料範圍是1e5,那麼這個圖最多可以有1e10的格子,而時間限制於1s,顯然用蒟蒻的解法會超時。
下面聊聊大佬的思路:
如果通過一個點去描述一張圖,這樣做的複雜度太大。那麼反過來,為什麼不可以把圖分割成一個個小部分,再通過探究每個點對這個區域性圖的影響來確定整個圖呢?
於是就有下面的程式碼:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define N 100000
ll n, m, k;
ll x[N], y[N],vis[N];
ll color[N];
ll ans = 0;
ll step = 0;
int main()
{
	cin >> n >> m >> k;
	for (int i = 1; i <= k; i++)
	{
		cin >> x[i] >> y[i];
		vis[x[i]] = 1;
	}
	for (int i = 1; i <= n; i++)
	{
		if (vis[i] == 1)
		{
			continue;
		}
		ll sum = m;
		//cout << 'm' << ' ' << m << endl;
		for (int j = 1; j <= k; j++)
		{
			//cout << "color[y[j]]= " << color[y[j]] << endl;
			if (color[y[j]] != i)sum--;
			//cout <<'1' <<' '<<sum << endl;
			color[y[j]] = i;//致命錯誤
			if (x[j] > i)
			{
				int len = x[j]-i;
				if (y[j] -1>= len&&color[y[j]-len]!=i)
				{
					sum--;
					color[y[j] - len] = i;
				}
				if (m - y[j] >= len && color[y[j] + len] != i)
				{
					sum--;
					color[y[j] + len] = i;
				}
			}
			//cout << '2'<<' '<<sum << endl;
			if(x[j]<i)
			{
				int len = i-x[j];//頂級錯誤
				if (y[j] -1>= len && color[y[j] - len] != i)
				{
					sum--;
				//	cout << y[j] - 1 << ' ' << len << endl;
			//		cout << "keneng chucuo" << endl;
					color[y[j] - len] = i;
				}
			    if (m - y[j] >= len && color[y[j] + len] != i)
				{
					sum--;
					color[y[j] + len] = i;
				}
			}
		//	cout <<'3'<< ' '<< sum << endl;
			
		}
		//cout << '4'<<' '<<sum << endl;
		ans += sum;
	}
	cout << ans << endl;
	return 0;
}

我們把一個棋盤分解成n個行,研究每個點對該行的影響,如果這個皇后斜著走會經過這行,那我們就標記這個點,當遍歷了所有點後,那些沒有標記的點就是皇后不會經過的點,這樣這一行的不能被經過點的個數就確定了。重複操作後就可以找到這樣圖的全部未經過點了。


其實這個思路不難理解,但是思路化為程式碼確實令人難受。換句話說,就是打碼一小時,除錯八小時。
下面就來聊聊我在這道題上放的小錯誤:
1.1 建圖的錯誤
一開始我認為的棋盤是這個樣子的:
在這裡插入圖片描述
但實際上是這個樣子的:
在這裡插入圖片描述
我記得這個建圖錯誤不是第一次遇到,但還是忘記了。


在解決了這個問題後,我覺得我一定能過了。但是垃圾oj又一次讓我感受到了社會的險惡,全面WA了。於是我開啟了瘋狂除錯之路,這一調又是3小時。
最後我是通過自己建圖來找錯的:
在這裡插入圖片描述
根據圖來看程式碼,最後發現我有幾處的 j 寫成了 i 。直接吐血的操作,哎。
由此題也告訴我們,最好不要讓j 和 i 出現在同一個部分,特別是 i 和 j 要不斷變化和使用的時候,因為一個不小心就是 8個小時調程式碼的歡樂時光。

相關文章