1. 程式人生 > >HDU 6205 card card card ( 思維 )

HDU 6205 card card card ( 思維 )

scan size 是否 for 個數 col 時間 print +=

題意 : 給定兩個序列 a 和 b ,保證 a 數列的和 == b數列的和,從頭到尾考慮 (a[i] - b[i]) 的前綴和,直到前綴和為負數則無法進行下去,所得的便是a[1~i]的和,現在有一個操作,就是你可以將最前面的a[1] && b[1] 這兩個數放到末尾去,問你最少經過多少次這樣的操作能夠使得去到的a[1~i]的的和是最大的

分析 : 發現因為有 a數列的和 == b數列的和,所以肯定有以某一個數開頭使得將所有序列的數全部取完即 sum( a[1~末尾] ),我們先構造出 sub[i] = a[i] - b[i] 的序列,然後每一次選取一個正的sub[i] 作為開頭來判斷是否可行,如果在 i~k-1都可行,但是加上sub[k]就變得不可行,那麽下一個開頭就不是離 sub[i] 右邊最近的一個正整數作為頭,而是離 sub[k] 右邊(當然最後一個元素的右邊是第一個元素) 最近的一個正整數作為開頭,為什麽這樣做呢?首先先如果按照第一種做法肯定是正確的,但是在時間上不是最優的,因為從 i~(k-1) 都是可行的,那麽如果下一個開頭的數還是在 i~(k-1) 這個序列裏面考慮的話,那麽下一次還是會在 k 這裏停止,因為 sub[i] 是正數,細想就可以想明白了。模擬這個操作可以將數組復制一邊粘到原數組末尾,但是這裏我用了%操作,其實都一樣。

技術分享
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int n, a[maxn], b, sub[maxn];
inline int Scan()
{
    int res = 0, ch;
    bool flag = false;
    if((ch=getchar()) == -) flag = true;
    else if(ch>=0 && ch<=9) res = ch-0;
    while((ch=getchar())>=
0 && ch<=9) res = res*10+ch-0; return flag?-res:res; } int main(void) { while(~scanf("%d", &n)){ for(int i=0; i<n; i++) a[i] = Scan(); int negative = 0; for(int i=0; i<n; i++){ b = Scan(); sub[i] = a[i] - b; if(sub[i] < 0
) negative++; } if(negative == n) { puts("0"); continue; }///全都是負數的情況 int sum = 0, pos = 0; while(true){ bool ok = true; while(sub[pos] < 0) pos++;///從當前位置開始找到右邊第一個正數 for(int i=pos, j=0; !(j!=0&&i==pos); j++, i=(i+1)%n){///開始累加前綴和sum,j的作用是輔助判斷i是否已經第二次到達了pos這個位置,也就是pos是滿足題意的! sum += sub[i];///累加前綴和 if(sum < 0){///如果小於0,則在這裏停止 ok = false; pos = i;///記錄一下當前位置 sum = 0;///重置前綴和 break; } } if(ok) break; } printf("%d\n", pos); } return 0; }
View Code

瞎 : 沈陽網絡賽的題,靠隊友帶飛,學習隊友代碼orz

HDU 6205 card card card ( 思維 )