应该一眼看出来是贪心题,然后想最优解是什么。正确的贪心策略是【原棋盘上每个位置的棋子】都往最近的左边【目标棋盘上棋子】移动,如果左边没有棋子了那就闲置最后处理,如果目标棋盘在该位置上也有棋子,那就算距离为0(最近)。最后处理的话,棋盘上的局面应该是所有的棋子都得往右移,这样的话怎么移都无所谓了,朴素的把放过去。
模拟的时候难度在于通过upper bound找到离该位置最近左的位置,所以复杂度是O(NlogN);如果朴素的遍历数组找最近左需要N复杂度,整体n^2就过不了了。
对于upper_bound来说,返回的是被查序列中第一个大于查找值的指针,也就是返回指向被查值>查找值的最小指针,lower_bound则是返回的是被查序列中第一个大于等于查找值的指针,也就是返回指向被查值>=查找值的最小指针。 【引用自 https://blog.csdn.net/u011008379/article/details/50725670 】
1 #include<iostream> 2 #include<algorithm> 3 #include<map> 4 #include<vector> 5 using namespace std; 6 7 int board[100005]; 8 vector<int> va; 9 map<int,int> m; 10 long long ans; 11 //贪心策略 12 //每个都向自己最近的左边的棋子移动 13 int main(){ 14 int n; cin>>n; 15 for(int i=1;i<=n;i++) cin>>board[i]; 16 for(int i=1;i<=n;i++){ 17 int x; cin>>x; 18 if(x){//这个位置上有棋子 19 va.push_back(i); 20 m[i]=x; 21 } 22 } 23 24 for(int i=1;i<=n;i++){ 25 while(1){ 26 vector<int>::iterator index = upper_bound(va.begin(),va.end(),i);/* index指向第一个大于i的元素 */ 27 //如果没有比i还大的,返回begin() 28 if(index==va.begin()) break;//目标盘左边没有棋子了 29 index--; 30 if( m[ *index ]>board[i] ) { 31 ans+=(i-(*index) )*board[i]; 32 m[*index]-=board[i]; 33 board[i]=0; 34 break; 35 } 36 if( m[ *index ]==board[i] ){ 37 ans+=(i-(*index))*board[i]; 38 va.erase(index); 39 board[i]=0; 40 break; 41 } 42 if( m[ *index]<board[i]){ 43 ans+=(i-(*index))*m[ *index]; 44 va.erase(index); 45 board[i]-=m[*index]; 46 } 47 } 48 } 49 50 for(int i=1;i<=n;i++){ 51 if(board[i]==0) continue; 52 while(1){ 53 if( m[ va[0] ]>board[i] ) { 54 ans+=(i+va[0]-2)*board[i]; 55 m[ va[0] ]-=board[i]; 56 break; 57 } 58 if( m[ va[0] ]== board[i] ){ 59 ans+=(i+va[0]-2)*board[i]; 60 va.erase(va.begin()); 61 break; 62 } 63 if( m[ va[0] ]<board[i]){ 64 ans+=(i+va[0]-2)*m[ va[0] ]; 65 va.erase(va.begin()); 66 board[i]-=m[ va[0] ]; 67 } 68 } 69 } 70 71 cout<<ans<<endl; 72 73 return 0; 74 }
没有提交在美团的oj上,但自己编了几个数据都过了。