Codeforces Round #256 (Div. 2)

OpenSoucre發表於2014-07-18

A - Rewards

水題,把a累加,然後向上取整(double)a/5,把b累加,然後向上取整(double)b/10,然後判斷a+b是不是大於n即可

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

int main(){
    double a1,a2,a3;
    double b1,b2,b3;
    int n;
    cin >>a1 >> a2 >> a3 >>b1 >> b2 >> b3>> n;
    int cnt = ceil((a1+a2+a3)/5)+ceil((b1+b2+b3)/10);
    if(cnt > n) cout <<"NO"<<endl;
    else cout<<"YES"<<endl;
}
View Code

B - Suffix Structures

題目的意思是:

  給兩個字串s和t,對字串s可以進行下面操作(注意每種操作可以進行無限次)

  • 刪除s的一個字元,如果只做這種操作,輸出automation
  • 交換s的任意兩個字元,如果只做這種操作,輸出array
  • 如果既要刪除又要交換,則輸出both
  • 如果上面操作無法完成,則輸出need tree

解題思路:

  (1)首先判斷s和t的長度,如果s的長度比t的長度小,則上面的操作都無法完成如果s的長度大於t的長度,則需要刪除字元

  (2)將s中不屬於t內的字元刪除,這剩下的字元為str

  (3)統計str和t中各自字串出現的次數,

      如果中的字元不在str中,即strCnt.size() < tCnt.size(),則上面操作無法完成。

      如果tCnt中某字元的個數大於strCnt的字元的個數,則上面的操作無法完成。

      如果strCnt中存在字元的個數大於tcnt的相應字元的個數,則必須要刪除s的字元。

      如果s的所有字元的個數和t的所有字元的個數相等,則只需要交換即可

  (4)最後通過在str中使用兩個指標i,index,i指向str,index指向t,遍歷str,如果t[index]==str[i],則index++; 最後判斷index是否指向t的末尾,如果是,則只需要刪除字元即可,否則既要刪除又要交換字元。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <map>
using namespace std;

int main(){
    string s,t;
    cin >>s >>t;
    bool needTree = false,automatonFlag = false;
    if(s.length() < t.length()) needTree = true;
    else if(s.length() >t.length()) automatonFlag = true;
    string str="";
    for(int i = 0 ; i < s.length(); ++ i ){
        if(t.find(s[i])!=string::npos) str+=s[i];
    }
    map<char,int> strCnt,tCnt;
    for(int i = 0 ; i < str.length(); ++ i) strCnt[str[i]]++;
    for(int i = 0 ; i < t.length(); ++ i) tCnt[t[i]]++;
    if(strCnt.size()!=tCnt.size()) needTree = true;
    else{
        for(map<char,int>::iterator iter = tCnt.begin(); iter!=tCnt.end(); iter++){
            if(iter->second > strCnt[iter->first]) {needTree = true;break;}
            if(iter->second < strCnt[iter->first]) automatonFlag = true;
        }
    }
    if(needTree) cout<<"need tree"<<endl;
    else{
        if(!automatonFlag) cout<<"array"<<endl;
        else{
            int i = 0,index = 0;
            for(int i = 0 ; i <  str.length() && index < t.length(); ++i){
                if(t[index] == str[i]) index++;
            }
            if(index < t.length()) cout<<"both"<<endl;
            else cout<<"automaton"<<endl;
        }
    }
}
View Code

C - Painting Fence

題目的簡化意思是:

  給你一個直方圖,每次只能用畫筆刷一行(注意是連在一起的)或者1豎,求至少要刷多少次能把這個直方圖刷完

 解題思路是

  每次取min(a1,a2,...an),然後水平刷min(a1,a2,...an)次,刷完後,相當於每個直方條少了ai-min(a1,a2....an)(i=0..n-1),這時候這些直方條就會被高度為0的直方條隔開,然後對每個部分,進行遞迴呼叫,算出次數,注意每次還要和right-left+1(相當於每直方條都刷一次)比較,然後取小值。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
int solve(vector<int>& a,int left, int right){
    if(right< left) return 0;
    int minv = *min_element(a.begin()+left,a.begin()+right+1);
    int res = minv, index = left;
    for(int i = left; i<=right; ++i) a[i]-=minv;
    for(int i = left; i<= right; ++ i){
        if(a[i] == 0 ){
            if(index < i) {
                res+=solve(a,index,i-1);
            }
            index = i+1;
        }
    }
    res+=solve(a,index,right);
    return min(res,right-left+1);
}

int main(){
    int n;
    cin >> n ;
    vector<int> a(n,0);
    for(int i = 0 ; i < n; ++ i) cin >> a[i];
    cout<<solve(a,0,n-1)<<endl;
}
遞迴實現

D - Multiplication Table

求二維陣列第k小值,用優先權佇列取每個元素的下面和右邊的元素插入佇列,結果超時。由於用優先權佇列其時間複雜度為klognm,而k<=nm則最壞時間複雜度是nmlog(nm),肯定超時

#include <iostream>
#include <vector>
#include <cmath>
#include <queue>
#include <functional>
#define LL long long

using namespace std;

struct Point{
    int row;
    int col;
    Point(int x=0, int y =0 ):row(x), col(y){}

    bool operator<(const Point& a) const{
        return (LL)row*(LL)col > (LL)a.row*(LL)a.col;
    }
};

int main(){
    int  n, m;
    LL k;
    cin >> n >> m >>k;
    vector<vector<bool> > visit(n+1,vector<bool>(m+1,false));
    priority_queue<Point> que;
    que.push(Point(1,1));
    visit[1][1] = true;
    LL res = 0;
    for(int i = 0 ; i <k; ++i){
        Point tmp = que.top();que.pop();
        res = (LL)tmp.row*(LL)tmp.col;
        Point a(tmp.row+1,tmp.col),b(tmp.row,tmp.col+1);
        if(a.row <= n && a.col<=m && !visit[a.row][a.col]){que.push(a);visit[a.row][a.col] = true;}
        if(b.row<=n && b.col<=m && !visit[b.row][b.col]) {que.push(b);visit[b.row][b.col] = true;}
    }
    cout<<res<<endl;

}
優先權佇列!!超時

現在利用二分搜尋。

假設對任意給一個數x,如何判斷nxm二維陣列中小於x的個數?

注意由於對第i行第j列的數是i*j,則j=x/i表示x在第i行的位置,也就是在該行第j列之前的位置都是小於x的

所以遍歷每行,然後累加min(m,x/i)即可,則即是小於x的個數。

#include <iostream>
#include <vector>
#include <algorithm>
#define LL long long

using namespace std;

LL n,m,k;

LL calc(LL x){
    LL cnt = 0;
    for(LL i = 1; i <= n; ++ i ) cnt += min(m,x/i);
    return cnt;
}

int main(){
    cin >> n >>m >>k;
    LL left = 1, right = n*m;
    while(left<=right){
        LL mid = (left+right)/2;
        if(calc(mid) >=k) right = mid-1;
        else left = mid+1;
    }
    cout<<right+1<<endl;
}
二分搜尋,時間複雜度O(nlog(nm))

E - Divisors

題目的意思:

  給定一個整數X,X0=[X], Xi = [...]由Xi-1各個數的因子組成,求Xk,注意Xk中元素的個數最多為100000

解題思路:

  本題先求出X1,即先求出X1的因子,然後對X每個因子進行深搜,直到k=0或者x=1,這樣求出每個因子的k,但深搜要注意剪枝,題目要求的Xk的因子數不多餘100000,故當搜尋的因子數到達100000,就把後面的因子剪掉。這樣可以避免超時

#include <iostream>
#include <vector>
#include <algorithm>
#include <vector>
#define LL long long
using namespace std;

LL limit = 1e5;
vector<LL> divs,res;

bool solve(LL x, LL k){
    if(k == 0 ||  x == 1){
        res.push_back(x);
        return res.size() >= limit;
    }
    for(int  i = 0 ;i < divs.size(); ++ i){
        if(divs[i] > x) break;
        if(x%divs[i] == 0){
            if(solve(divs[i],k-1)) return true;
        }
    }
    return false;
}

int main(){
    LL x,k;
    cin >> x >> k;
    for(LL i = 1; i*i <= x; ++ i){
        if(x%i == 0){
            divs.push_back(i);
            if(i*i != x) divs.push_back(x/i);
        }
    }
    sort(divs.begin(),divs.end());
    solve(x,k);
    for(int i = 0 ; i < res.size(); ++ i){
        if(i) cout<<" ";
        cout<<res[i];
    }
    cout<<endl;
    return 0;
}
深度搜尋+剪枝

相關文章