信息学奥赛一本通 1322:【例6.4】拦截导弹问题(Noip1999)
【題目鏈接】
ybt 1322:【例6.4】攔截導彈問題(Noip1999)
原題有兩個問,本題為第2問
【題目考點】
1. 貪心
【解題思路】
設hhh表示當前系統可以攔截的最高的高度。
貪心選擇:高度小于等于hhh的導彈中高度最高的導彈。
如果當前系統無法再攔截導彈,而且存在未被攔截的導彈,那么增加一個攔截系統,將hhh設為無窮大,繼續進行貪心選擇。
各導彈高度構成一個數字序列,每個攔截系統攔截的導彈的高度為原序列的不升子序列。該題可以抽象為:求一個數字序列的不升子序列的最少個數。
以下討論中使用抽象后的概念。
1. 貪心選擇性質的證明:
貪心選擇:選擇小于等于hhh的最大數字,而后將hhh設為該數字。如果不存在,開新的序列,將hhh設為無窮大。
該題的解為多個不升數字序列。
證明:存在最優解包含第一次的貪心選擇,也就是說最大值ggg是某個數字序列的第一個數字。
假設所有的最優解都中ggg都不是第一個數字,
某最優解中,存在子序列a1,a2,...,g,...,ama_1, a_2, ...,g,..., a_ma1?,a2?,...,g,...,am?,由于該子序列為不升子序列,那么a1≤a2≤...≤ga_1 \le a_2 \le ... \le ga1?≤a2?≤...≤g,而ggg是所有數字中的最大值,因而有g≥a1g \ge a_1g≥a1?,所以a1=ga_1 = ga1?=g,該序列的第一個數字就是ggg,與假設相悖,原命題得證。
證明:假設前k次都進行貪心選擇,存在最優解包含第k+1次的貪心選擇ggg。
1) 如果ggg是某子序列的第一個數
假設所有的最優解都不包含第一個數是ggg的子序列,任選一個最優解,存在在第k次選擇后選擇到的數字構成的子序列a1,a2,...,g,...,ama_1, a_2, ...,g,..., a_ma1?,a2?,...,g,...,am?,由于該子序列為不升子序列,那么a1≥a2≥...≥ga_1 \ge a_2 \ge ... \ge ga1?≥a2?≥...≥g,而ggg是第k次選擇后選擇的數字中的最大值,因而有g≥a1g \ge a_1g≥a1?,所以a1=ga_1 = ga1?=g,該序列的第一個數字就是ggg,與假設相悖,原命題得證。
2)如果ggg不是某子序列的第一個數
也就是說,存在子序列ax,ax+1,...,aka_x, a_{x+1},... ,a_kax?,ax+1?,...,ak?,aka_kak?是第k次的貪心選擇,前k次的貪心選擇已經固定。下一次貪心選擇是ggg,應該與aka_kak?在同一序列且在aka_kak?的后面。
假設所有最優解中不存在aka_kak?在子序列中的下一個數字是ggg的情況。
1.如果aka_kak?與ggg仍然在同一子序列,那么有:ax,...,ak,ak+1,...,g,...,ama_x,... ,a_k, a_{k+1}, ...,g, ..., a_max?,...,ak?,ak+1?,...,g,...,am?
已知g是第k次貪心選擇后選擇的數字中的最大數字,那么有g≥ak+1g \ge a_{k+1}g≥ak+1?,因為這是不升序列,那么有ak+1≥ga_{k+1} \ge gak+1?≥g,所以ak+1=ga_{k+1} = gak+1?=g,aka_kak?的下一個數字是ggg,與假設相悖。
2.如果aka_kak?與ggg不在同一子序列中,
ggg不可能接在由前k次貪心選擇構成的老序列的后面。如果能接在老序列的后面,之前做貪心選擇時就會選到ggg。
因而ggg所在的序列一定是由第k次貪心選擇之后選擇到的數字組成的,可能為:ay,ay+1,...,ag?1,g,ag+1,...,agea_y, a_{y+1}, ..., a_{g-1}, g, a_{g+1}, ..., a_{ge}ay?,ay+1?,...,ag?1?,g,ag+1?,...,age?。這是不升序列,所以ay≥ga_y \ge gay?≥g
由于ggg是第k次貪心選擇后剩余數字中的最大數字,所以ay≤ga_y \le gay?≤g,所以有ay=ga_y = gay?=g。
因此g一定是某個序列中的第一個數字,該序列為:g,ag?1,g,ag+1,...,ageg, a_{g-1}, g, a_{g+1}, ..., a_{ge}g,ag?1?,g,ag+1?,...,age?
第k次貪心選擇aka_kak?存在的序列為ax,ax+1,...,ak,ak+1,...,ama_x, a_{x+1},... ,a_k, a_{k+1}, ..., a_max?,ax+1?,...,ak?,ak+1?,...,am?(也可能不存在ak+1,...,ama_{k+1}, ..., a_mak+1?,...,am?),
由于g≤akg\le a_kg≤ak?,序列ax,ax+1,...,ak,g,ag+1,...,agea_x, a_{x+1},... ,a_k, g, a_{g+1}, ..., a_{ge}ax?,ax+1?,...,ak?,g,ag+1?,...,age?一定也是不升序列。
如果ak+1,...,ama_{k+1}, ..., a_mak+1?,...,am?不存在,則將ggg所在的序列接在aka_kak?的后面,構成序列ax,ax+1,...,ak,g,ag+1,...,agea_x, a_{x+1},... ,a_k, g, a_{g+1}, ..., a_{ge}ax?,ax+1?,...,ak?,g,ag+1?,...,age?,總序列數量減少,仍然是最優解。
如果存在ak+1,...,ama_{k+1}, ..., a_mak+1?,...,am?,那么將序列g,ag+1,...,ageg, a_{g+1}, ..., a_{ge}g,ag+1?,...,age?與ak+1,...,ama_{k+1}, ..., a_mak+1?,...,am?交換。得到序列ax,ax+1,...,ak,g,ag+1,...,agea_x, a_{x+1},... ,a_k, g, a_{g+1}, ..., a_{ge}ax?,ax+1?,...,ak?,g,ag+1?,...,age?與ak+1,...,ama_{k+1}, ..., a_mak+1?,...,am?,總序列數量不變,仍是最優解。
無論何總情況,總能構造出滿足aka_kak?在子序列中下一個數字是ggg的最優解。與假設相悖,假設不成立,原命題得證。
2. 具體做法
將導彈高度記錄在數組a中,設vis數組,表示第i個導彈是否已經被攔截。設h為無窮大。順序遍歷數組a,遇到小于等于h的數字,則攔截該導彈,記錄攔截導彈的個數。
遍歷結束后,如果攔截到的導彈數量不足n,那么將h設為無窮大,再次遍歷該數組。重復上述過程,直到攔截的數量等于n。輸出遍歷的次數。
【題解代碼】
解法1:貪心
#include<bits/stdc++.h> using namespace std; #define N 1005 #define INF 0x3f3f3f3f int main() {int a[N], n = 1, ct = 0, h, ans = 0;//ct:攔截的導彈數量 ans:遍歷了幾趟 bool vis[N] = {};while(cin >> a[n])n++;n--;while(ct < n){h = INF;for(int i = 1; i <= n; ++i){if(vis[i] == false && h >= a[i])//如果導彈i沒被攔截 且 可以被攔截 {h = a[i];vis[i] = true;ct++;}}ans++;}cout << ans;return 0; }總結
以上是生活随笔為你收集整理的信息学奥赛一本通 1322:【例6.4】拦截导弹问题(Noip1999)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: erp采购总监个人总结_2018计划工作
- 下一篇: 的正确使用_如何正确使用安全带 安全带正