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; }
出处:http://www.cnblogs.com/lightsong/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。