1. 程式人生 > 其它 >One-Dimensional Battle Ships題解

One-Dimensional Battle Ships題解

題目翻譯

\(Alice\)\(Bob\) 喜歡在 \(1×n\) 的表格中玩戰艦遊戲。遊戲開始時,\(Alice\)\(k\) 艘戰艦,每艘戰艦長度為 \(a\) ,她需要把這些戰艦不重疊且不相鄰地放在格子中(不允許有兩艘戰艦的格子存在公共邊)。但她並不會告訴 \(Bob\) 她放的位置。

接下來,\(Bob\) 會用 \(m\) 顆炮彈嘗試打中 \(Alice\) 的戰艦,每顆炮彈會選擇一個格子打擊。但由於 \(Alice\) 喜歡作弊,所以她不會告訴 \(Bob\) 什麼時候擊中了戰艦。請你幫助 \(Bob\) 判斷,在第幾次發射炮彈後,\(Alice\) 一定會有一艘戰艦被擊中。

思路詳解

這道題其實普遍的方法就兩種, 因為 STRADUST 大佬寫了二分做法, 所以我就來寫一下 STL 做法。(主要原因還是現在在學 STL , 畢竟學了什麼就要用什麼)(如想檢視二分做法, 點這裡

迴歸正題, 上文已經說了我們要用 STL 來解決這道題, 那麼, 怎麼解決呢?

首先, 我們考慮一個區間 \([l, r]\) 。如果我們想在這個區間裡面放戰艦, 那麼就根據排列組合, 既然兩艘艦艇不能挨在一起, 那麼就考慮往每艘艦艇的左邊都多佔一個, 這樣就可以兩條戰艦挨在一起了。 但這樣就會出現最左邊的戰艦貼著左邊, 所以我們需要在區間 \([l, r]\) 的左邊再加一格, 來保證貼左的問題。 所以, 這個區間的最終的 \(MAX\{可放置艦艇數量\} = (r - l + 1 + 1) / a (a 如題目所示)\)

。接下來的問題就容易的多了。

我們記錄一下整個的最大可放置艦艇數為 \(smax\) 。對於每次對於一個獨立區間 \([l, r]\) 的轟炸, 假設轟炸位置為 \(mid\) , 因為 \(mid\) 處不能有戰艦了, 所以原來的區間 \([l, r]\) 也就變成了兩個單獨的能放置戰艦的區間 \([l, mid - 1]\)\([mid + 1, r]\) 。那麼, 這次操作對 \(smax\) 的改動為 \(smax - (r - l + 1 + 1) / a + (mid - 1 - l + 1 + 1) / a + (r - mid - 1 + 1 + 1) / a\) 。這樣做似乎有點難用 STL , 那我們再將剛才涉及的那三個區間拿出來變個形: \((l - 1, r + 1), (l - 1, mid), (mid, r + 1)\)

。 變形過後的區間就很容易發現, 其實每次操作過後的區間, 都是可以用一個 set 來儲存端點的。 其原因就是每個區間中間一旦發生一次操作, 這個區間就不會再存在, 只會留下兩個小的區間了。那麼, 就可以想到, 每次進行轟炸的時候, 都找到自己所在的區間, 也就等價為找到自己所在的區間的左端點 (在 set 中在此位置之前第一個編號小於這個位置的位置)和右端點 (同理), 然後更改一下最多可放置艦艇數再把這個位置丟進 set 中。 如果那次操作後最多可放置艦艇數小於了 \(k\) , 那麼這次操作 \(Alice\) 就在撒謊。

程式碼:

#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
 
set <int> s1;
 
void read (int &a) {
	a = 0;
	
	char c = getchar ();
	bool bl = 0;
	
	while (c < '0' || c > '9') {
		if (c == '-') {
			bl = 1;
		}
		c = getchar ();
	}
	while (c >= '0' && c <= '9') {
		a = (a << 1) + (a << 3) + (c ^ 48);
		c = getchar ();
	}
	a = bl ? -a : a;
}
void read (long long &a) {
	a = 0;
	
	char c = getchar ();
	bool bl = 0;
	
	while (c < '0' || c > '9') {
		if (c == '-') {
			bl = 1;
		}
		c = getchar ();
	}
	while (c >= '0' && c <= '9') {
		a = (a << 1) + (a << 3) + (c ^ 48);
		c = getchar ();
	}
	a = bl ? -a : a;
}
 
int main () {
	int n, k, a;
	int m;
	int sum = 0;
	
	read (n), read (k), read (a);
        //初始區間
	s1.insert(n + 1);
	s1.insert(0);
	sum = n / a;
	read (m);
	for (int l = 1; l <= m; l ++) {
		int x;
		
		read (x);
		s1.insert(x);
		//找到所在區間
		set <int> :: iterator i = s1.lower_bound(x), j = s1.upper_bound(x);
		
		i --;
		sum -= (*j - *i - 1) / a;
		sum += (x - *i - 1) / a;
		sum += (*j - x - 1) / a; 
		if (sum < k) {//判斷是否撒謊
			printf ("%d", l);
			
			return 0;
		}
	}
	printf ("-1");
}