借教室-洛谷-P1083
原題: 在大學期間,經常需要租借教室。大到院系舉辦活動,小到學習小組自習討論,都需要向學校申請借教室。教室的大小功能不同,借教室人的身份不同,借教室的手續也不一樣。 面對海量租借教室的資訊,我們自然希望程式設計解決這個問題。 我們需要處理接下來n天的借教室資訊,其中第i天學校有ri個教室可供租借。共有m份訂單,每份訂單用三個正整數描述,分別為dj,sj,ti,表示某租借者需要從第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借djdj個教室。我們假定,租借者對教室的大小、地點沒有要求。即對於每份訂單,我們只需要每天提供djdj 個教室,而它們具體是哪些教室,每天是否是相同的教室則不用考慮。借教室的原則是先到先得,也就是說我們要按照訂單的先後順序依次為每份訂單分配教室。如果在分配的過程中遇到一份訂單無法完全滿足,則需要停止教室的分配,通知當前申請人修改訂單。這裡的無法滿足指從第sj 天到第tj天中有至少一天剩餘的教室數量不足dj個。現在我們需要知道,是否會有訂單無法完全滿足。如果有,需要通知哪一個申請人修改訂單。 輸入輸出格式 輸入格式: 第一行包含兩個正整數n,m,表示天數和訂單的數量。 第二行包含n個正整數,其中第i個數為ri,表示第ii天可用於租借的教室數量。 接下來有m行,每行包含三個正整數dj,sj,tj,表示租借的數量,租借開始、結束分別在第幾天。 每行相鄰的兩個數之間均用一個空格隔開。天數與訂單均用從1開始的整數編號。 輸出格式: 如果所有訂單均可滿足,則輸出只有一行,包含一個整數 0。否則(訂單無法完全滿足) 輸出兩行,第一行輸出一個負整數-1,第二行輸出需要修改訂單的申請人編號。 輸入輸出樣例 輸入樣例#1: 4 3 2 5 4 3 2 1 3 3 2 4 4 2 4 輸出樣例#1: -1 2 說明 【輸入輸出樣例說明】 第1份訂單滿足後,4天剩餘的教室數分別為 0,3,2,3。第 2份訂單要求第 2 天到第 4天每天提供 3 個教室,而第 3 天剩餘的教室數為 2,因此無法滿足。分配停止,通知第2 個申請人修改訂單。 【資料範圍】 對於 100%的資料,有1 ≤ n,m ≤ 10^6,0 ≤ r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n1≤n,m≤10 題意: 有n間教室,有m個任務,每一個讓任務包含了該任務的需要借的教室數量,以及借教室的起始時間和結束時間。 問能不能夠滿足所有任務,若是能則輸出0,若是不能則輸出-1,和不能夠滿足的任務編號。 題解: 這道題目,開始以為是一道模擬水題,暴力求解了結果只得了40分,TLE了很多點。 之後看了標程後才發現這道題目,也可以用二分來做。 下面講講二分的具體做法: 這道題目要求的是不能滿足的任務編號,所以我們可以二分求解最小不能滿足的任務編號。判斷函式中用到了差分陣列diff【】,其實就是字首和的逆陣列(其實在這道題目裡和普通陣列一樣)。每一個diff【i】存放的是第i天所需要的教室總數。之後再統一判斷,(這裡就是這道題降低時間複雜度的地方,他不需要對於每一個任務都判斷是否符合,而是統一求解,判斷出不能滿足任務編號的區間)。若是所需教室數量大於教室總數,則證明再left-mid之間有不能夠滿足的編任務,反之則證明不能滿足的任務在mid-right之間。(註解:開始可以提前判斷m個任務是否都能滿足,若是能夠滿足最直接輸出0,若是不能則證明有不能夠滿足的任務,再進入二分迴圈)。 附上AC程式碼:
#include <iostream> #include <cstring> using namespace std; int n,m,r[1000005],need[1000005],ans=2000000,diff[1000005]; struct task { int d,st,ed; }t[1000005];//任務陣列,d代表借用時間,st起始時間,ed結束時間 int check(int x) { memset(diff,0,sizeof(diff));//差分陣列 for(int i=1;i<=x;i++) { diff[t[i].st]+=t[i].d;//該任務的起始時間需要d間教室 diff[t[i].ed+1]-=t[i].d;//該任務結束了就需要釋放掉d間教室 } for(int i=1;i<=n;i++) { need[i]=need[i-1]+diff[i];//記錄n天中每一天需要的教室數量 if(need[i]>r[i])return 0;//如果當天需要教室數量大於當天擁有的教室數量則證明在left-mid之間存在不能夠滿足的任務 } return 1;//反之,則證明在left-mid之間的任務都能夠滿足 } int main() { cin>>n>>m; for(int i=1;i<=n;i++) { cin>>r[i]; } for(int i=1;i<=m;i++) { cin>>t[i].d>>t[i].st>>t[i].ed; } int left=1,mid,right=m;//初始化,因為所有可能出現的不能完成任務編號只能在1-m之間。 if(check(m)){cout<<0<<endl;return 0;}//判斷是否都能滿足 while(left<right) { mid=(right+left)/2; if(check(mid)) left=mid+1;//返回值是1代表在left-mid之間的任務都能滿足 else right=mid;//返回值是0代表在left-mid之間有不能滿足的任務 } cout<<-1<<endl; cout<<left<<endl; return 0; }
歡迎評論!