Stay Hungry,Stay Foolish!

E - Dividing Chocolate --- detecting next end point by double expand & half fold way

E - Dividing Chocolate

https://atcoder.jp/contests/abc159/tasks/abc159_e

 

思路

对于不大于k位置的查找,

前一篇给出了基于前缀和的 从左向右逐步查找方法

https://www.cnblogs.com/lightsong/p/17124860.html

 

对于上规模的w情况, 即w很大的情况,

同时白块比较稀疏, 每次向右移动一格的方法, 过于低效

 

本文给出启发式搜索方法:

以向右探测的步骤作为观察标准 presee

初始设置 presee 为 1

如果不大于x,

presee扩大二倍,

如果还是不大于x

presee再扩大二倍

 

否则,如果大于x, 则presee缩小一半

如果还是大于x,则presee再缩小一半

直至为0

 

类似TCP超时重试的机制。

参考:

https://atcoder.jp/contests/abc159/submissions/38882293

 对于此代码的解决方案, 文末给出详细的注释版代码。

 

Code

https://atcoder.jp/contests/abc159/submissions/38928832

 
int h, w, k;
vector<vector<int>> s(16, vector<int>(1006, 0));
//int s[16][1006];
 
// prefix sum
vector<vector<int>> ps(16, vector<int>(1006, 0));
//int ps[16][1006];
 
int main()
{
    cin >> h >> w >> k;
 
    REP(i, h){
        int hi = i+1;
 
        REP(j, w){
            int wj = j+1;
 
            char one;
            cin >> one;
 
            s[hi][wj] = (one=='1'?1:0);
 
            ps[hi][wj] = s[hi][wj] + ps[hi-1][wj] + ps[hi][wj-1] - ps[hi-1][wj-1];
        }
    }
 
    if (ps[h][w] <= k){
        cout << 0 << endl;
        return 0;
    }
 
    int mincuts = (h-1) * (w-1);
 
    // horizontally split
    for(int i=0; i<(1<<(h-1)); i++){
//        cout << "----------- combination i = " << i << " ----------" << endl;
 
        // figure out cuts of horizons
        vector<int> hcuts;
        for(int j=1;j<h; j++){
//            cout << "j = " << j << endl;
            if((i>>(j-1)) & 1){
                hcuts.push_back(j);
            }
        }
 
//
//        cout << "------ hcuts: combination individual cases --------" << endl;
//        REP(j, hcuts.size()){
//            cout << hcuts[j] << " ";
//        }
//        cout << endl;
 
        /* hlines: 0 1 3
        ------ 0
        1 2 3
        ------ 1
        4 5 6
        7 8 9
        ----- 3
        */
 
        vector<int> hlines;
        hlines.push_back(0);
        REP(j, hcuts.size()){
            hlines.push_back(hcuts[j]);
        }
        hlines.push_back(h);
//
//        cout << "--------- hlines --------" << endl;
//        REP(j, hlines.size()){
//            cout << hlines[j] << " ";
//        }
//        cout << endl;
 
        // vertically split
        /*
        vlines: 0 1 3
        |1|2 3|
        |4|5 6|
        |7|8 9|
        0 1   3
        */
 
        vector<int> vlines;
        vlines.push_back(0);
        bool hcuts_abort = false;
        
        while(true){
            int vline_start = vlines[vlines.size()-1];
            
            int curpos = vline_start;
            int presee = 1;
 
            while(presee){
                int vline_presee = curpos + presee;
 
//                cout << "try presee, it value=" << presee << endl;
                int hlines_len = hlines.size();
                
                /*
                --------- 0
                |1|2 3|
                --------- 1
                |4|5 6|
                |7|8 9|
                --------- 3
                0 1   3
                */
                bool greatk = false;
                
                for(int i=1; i<hlines_len; i++){
                    int hline_start = hlines[i-1];
                    int hline_end = hlines[i];
 
//                    cout << "hline_start=" << hline_start << endl;
//                    cout << "hline_end=" << hline_end << endl;
 
                    int rectsum = ps[hline_end][vline_presee]
                        - ps[hline_end][vline_start]
                        - ps[hline_start][vline_presee]
                        + ps[hline_start][vline_start];
 
//                    cout << "rectsum = " << rectsum << endl;
 
                    if (rectsum>k){
                        greatk = true;
                        break;
                    }
                }
 
                if(greatk){
                    presee>>=1;
                } else {
                    // for presee step, no sections sum great than k
                    // change current position to presee position
                    curpos = vline_presee;
                    
                    // increase presee step by 2 times
                    presee<<=1;
                    if (curpos+presee >= w){
                        presee = w - curpos;
                    }
                }
            }
 
            if (curpos == vline_start){
                hcuts_abort = true;
                break;
            } else {
                if (curpos == w){
                    break;
                }
                
                vlines.push_back(curpos);
            }
        }
 
        if(hcuts_abort){
//            cout << "----------- combination i = " << i << " aborted ----------" << endl;
            continue;
        }
//
//        cout << "----------- combination i = " << i << " met, its solution ----------" << endl;
//
//        cout << "--------- hlines --------" << endl;
//        REP(j, hlines.size()){
//            cout << hlines[j] << " ";
//        }
//        cout << endl;
//
//        cout << "--------- vlines --------" << endl;
//        REP(j, vlines.size()){
//            cout << vlines[j] << " ";
//        }
//        cout << endl;
 
        int cuts_num = hlines.size() - 2 + vlines.size() - 1;
//        cout << "----- current combination cuts ------";
//        cout << cuts_num << endl;
 
        mincuts = min(mincuts, cuts_num);
    }
 
    cout << mincuts << endl;
 
    return 0;
}
 

 

参考方案详解

https://atcoder.jp/contests/abc159/submissions/38882293

// Code 2
// O(2^H*(log W)*H)
#include<bits/stdc++.h>
using namespace std;
const bool ZJS_AK_IOI=1;//fast read&write or not
int n,m,x;

char c[50][1020];

int s[50][1020];
bool hlines_used[50];

int ans=INT_MAX;

void solve()
{
    cin>>n>>m>>x;
    
    /*
    for every row, calculate prefix sum from left to right
    for example
    1 2 3
    --->
    1 3 6
    */
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>c[i][j];
            s[i][j]=s[i][j-1]+(c[i][j]=='1');
            // cout<<s[i][j]<<" \n"[j==m];
        }
    }
    
    /*
    regarding to every case of row combination,
    look into splitting it vertically
    
    explaination for 1 << n-1
    n-1 : there are n-1 lines for cutting
    for example:
    1 2 3
    -------- line one for cutting
    4 5 6
    -------- line two for cutting
    7 8 9
    
    1<<n-1 : there are 2^(n-1) cases of row combination
    for example:
    4 cases for cutting regarding to 3 rows
    
    list:
    empty
    line one
    line two
    line one & line two
    
    bits presentation:
    1 1 == n-1 == 2
    0 0 --- empty
    0 1 --- line one
    1 0 --- line two
    1 1 --- line one & line two
    
    k is the bits presetation for every case
    */
    for(int k=0;k<(1<<n-1);k++)
    {
        /*
        how many lines are selected for cutting
        */
        int cuts_num=0;
        
        /*
        hlines_used arrary to record which lines are in the this combination case
        h means horizontal
        hlines_used[1] -- line one
        hlines_used[2] -- line two
        .....
        */
        memset(hlines_used,0,sizeof(hlines_used));
        
        /*
        kk is the power(2^kk) of line in the bits presentation
        such as
        1 1
        line one is corresponding to later one, with the power 0 -- 01 = 0*2^1 + 1*2^0
        line two is corresponding to former one, with the power 1 -- 10 = 1*2^1 + 0*2^0
        */
        for(int kk=0;kk<n;kk++)
        {
            /*
            k is the bits presentation of row combination case
            decrease the power of k by kk
            it means making the bit of kk power to rightmost position
            for example:
            1010 >> 1 ==> 101
            then we can use 101&1 == 1 to test if the power kk bit position are occupied
            i.e. line kk+1 is selected for cutting
            so set hlines_used[kk+1] = 1
            */
            if((k>>kk)&1)
            {
                hlines_used[kk+1]=1;
                cuts_num++;
            }
            // cout<<f[kk+1]<<" \n"[kk==n-1];
        }
        
        /*
        Is there one possible way to cut vertically
        i.e. cn find vertical cuts
        such as
        1 2 | 3
        4 5 | 6
        7 8 | 9
        */
        bool vcuts_ok=1;
        
        int lst=0;
        int p=1,step=0;
        
        while(step<m)
        {
            while(p)
            {
                /*
                white_num:
                how many white blocks are counted in one reactangle
                for example, there are four rectangles by this cuts:
                1 2 | 3
                -------
                4 5 | 6
                7 8 | 9
                */
                int white_num=0;
                
                /*
                Is the white blocks not great than x
                */
                bool not_great_x = true;
                
                /*
                for all horizontal cuts sections,
                decide if all sections white blocks number is not great than x
                from 1st postion to step+p
                */
                for(int i=1;i<=n;i++)
                {
                    white_num += s[i][step+p] - s[i][lst];
                    
                    if(white_num>x)
                    {
                        not_great_x = false;
                        break;
                    }
                    
                    if(hlines_used[i]){
                        white_num=0;
                    }
                }
                
                // cout<<step+p<<" "<<lst<<" "<<cnt<<endl;
                /*
                not_great_x == true
                means
                for (1st, step+p] interval of width direction
                the white block numbers of all horizontal cuts sections are not great than x
                then we can move step much further
                for example:
                1st = 0
                step = 0
                p = 1

                1 2 3
                -------
                4 5  6
                7 8  9

                there are two horizontal cuts sections
                section one:
                1 2 3
                
                section two:
                4 5  6
                7 8  9

                for section one, it will count one element 1
                |1| 2 3

                for section two, it will count two element 4 7
                |4| 5  6
                |7| 8  9
                */
                if(not_great_x)
                {
                    /*
                    change current position to predicted postion
                    step -> step + p
                    */
                    step += p;
                    
                    if(step==m){
                        break;
                    }

                    /*
                    beacuse it is ok for (1st, step+p] interval
                    we want to enlarge this interval to increase efficiency
                    like gredient descent algorithm
                    policy is to enlarge by multipling by two
                    */
                    p = min(p<<1, m-step);
                }
                else
                {
                    p>>=1;
                    
                    if(p==0)
                    {
                        if(step==lst){
                            vcuts_ok=false;
                        }

                        break;
                    }
                }
            }
            
            if(!vcuts_ok){
                break;
            }
            
            lst=step;
            p=1;
            
            cuts_num++;
        }
        
        if(vcuts_ok){
            ans=min(ans,cuts_num);
        }
    }
    
    cout<<ans-1<<endl;
}

int main()
{
    if(ZJS_AK_IOI){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);}
    // int t;cin>>t;while(t--)
    solve();
    return 0;
}

 

优化版

https://atcoder.jp/contests/abc159/submissions/38958177

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<b;i++)
int dp[16][1006];

int main(){
    int h,w,k;

    cin>>h>>w>>k;

    vector<string> v(h+1);

    for(int i=1;i<=h;i++){
        string s;
        cin>>s;
        v[i]+=' ';
        v[i]+=s;
//        cout<<v[i]<<endl;
    }

    for(int i=1;i<=h;i++){
        for(int j=1;j<=w;j++)
        {
            dp[i][j]=dp[i][j-1]+v[i][j]-'0';
//            cout<<dp[j][i]<<" ";
        }
//        cout<<endl;
    }

    int ans=INT_MAX;
    int n=pow(2,h-1);
    
    for(int i=0;i<n;i++){
//        cout << "---------------------------------------" << endl;
        int cuts_num = 0;

        // horizontal cuts
        bool hcuts[15]={false};
        hcuts[0]=true;
        hcuts[h]=true;
        
        int j=1;
        for(int num=i; num>0; num/=2){
            if(num%2==1){
//                cout << "horizontal cut = " << j << endl;
                cuts_num++;
                hcuts[j] = true;
            }
            
            j++;
        }

//        cout << "hcuts number = " << cuts_num << endl;

        // left bound
        int lbound = 0;
        // cannot cut vertically
        int vcannot_cut = false;

        while(lbound < w){
            int curpos=0;
            int presee=1;
            
//            cout << "lbound = " << lbound << endl;
            
            while(presee){
                int white_cnt = 0;
                bool great_k = false;
                
                for(int i=1; i<=h; i++){
                    white_cnt += dp[i][curpos+presee]- dp[i][lbound];
                    
                    if(white_cnt>k)
                    {
                        great_k=true;
                        break;
                    }
                    
                    if(hcuts[i]){
                        white_cnt=0;
                    }
                }
                
                if(great_k){
                    presee >>= 1;
                }else{
                    curpos += presee;
                    presee = min(presee*2, w-curpos);
                }
            }

            if(curpos == lbound){
                vcannot_cut = true;
                break;
            }
            
            lbound = curpos;
            presee = 1;
            
            /* to right end */
            if (lbound == w){
                break;
            }
            
            cuts_num++;
        }
        
        if(vcannot_cut){
            continue;
        }
        
//        cout << "cuts_num = " << cuts_num << endl;
        
        ans = min(ans, cuts_num);
    }

    cout<<ans<<endl;
}
posted @ 2023-02-16 21:47  lightsong  阅读(27)  评论(0编辑  收藏  举报
Life Is Short, We Need Ship To Travel