USACO Stringsobits, 還是得搬出動態規劃來

這題證明俺現在真是老了,退步了,這是第一份代碼:

  1. #include <fstream>
  2. using namespace std;
  3.  
  4. ifstream fin("kimbits.in");
  5. ofstream fout("kimbits.out");
  6.  
  7. unsigned int N, L, pos;
  8.  
  9. int main()
  10. {
  11.     fin >> N >> L >> pos;
  12.  
  13.     unsigned int num = 0, count = 0, max = (1<<N)-1, bit1, tmp;
  14.     while(count < pos)
  15.     {
  16.         if(num > max) break;
  17.         bit1 = 0, tmp = num;
  18.         for(; tmp > 0; bit1++)
  19.             tmp &= (tmp - 1);
  20.         if(bit1 <= L) ++count;
  21.         ++num;
  22.     }
  23.  
  24.     num -- ;
  25.     for (int i = N-1; i >=0; --i)
  26.         fout << ((num >>i) & 0x1u);
  27.     fout << endl;
  28.     return 0;
  29. }

其實還是挺簡潔的哈,就是會超時,因爲基本思路是暴力搜索。


這個是很鬱悶的,看數據,確實太大了。仔細端詳一番,覺得有DP的感覺,例如,對於(二進制)二位數:10 11,要得到三位數,實際上就是移位和移位+1,+1的操作中可以判斷+1後得到的數字中的1是不是超過了給定的L,不過,首先還是先崩潰了一下:

  1. #include <fstream>
  2. #include <deque>
  3. #include <vector>
  4. using namespace std;
  5.  
  6. ifstream fin("kimbits.in");
  7. ofstream fout("kimbits.out");
  8.  
  9. typedef unsigned int UINT;
  10. UINT N, L, pos;
  11. vector<deque<UINT> > dp;
  12.  
  13. inline int printBinary(UINT num)
  14. {
  15.     for (int i = N-1; i >=0; --i)
  16.         fout << ((num >>i) & 0x1u);
  17.     fout << endl;
  18.     return 0;
  19. }
  20.  
  21. int main()
  22. {
  23.     fin >> N >> L >> pos;
  24.  
  25.     if(pos <= 2) {
  26.         printBinary(pos-1);
  27.         return 0;
  28.     }
  29.  
  30.     deque<UINT> d, dPre;
  31.     d.push_back(0);
  32.     for(UINT i = 0; i <= N; ++i)
  33.         dp.push_back(d);
  34.  
  35.     dp[0][0] = 1,  dp[0].push_back(1);
  36.     UINT count = 2, uTmp;
  37.  
  38.     for(UINT i = 0; i < N; ++i)
  39.     {
  40.         for(UINT k = 0; k <= i; ++k)
  41.         {
  42.             d.clear();
  43.             for(UINT j = 1; j <= dp[k][0]; ++j)
  44.             {
  45.                 uTmp = (dp[k][j] << 1);
  46.                 if(++count == pos) return printBinary(uTmp);
  47.  
  48.                 if(k+2 <= L) // k從0開始的
  49.                 {
  50.                     uTmp = (dp[k][j]<<1)+1;
  51.                     if(++count == pos) return printBinary(uTmp);
  52.                     d.push_back(uTmp);
  53.                 }
  54.  
  55.                 dp[k][j] <<= 1;
  56.             }
  57.             if(== i)
  58.             {
  59.                 dp[k+1].insert(++dp[k+1].begin(), d.begin(), d.end());
  60.                 dp[k+1][0] += d.size();
  61.             }
  62.             if(> 0)
  63.             {
  64.                 dp[k].insert(++dp[k].begin(), dPre.begin(), dPre.end());
  65.                 dp[k][0] += dPre.size();
  66.             }
  67.             dPre = d;
  68.         }
  69.     }
  70.  
  71.     return 0;
  72. }

時間還不知道,空間先扛不住了:


看Test 6和7,估計這個辦法也不咋滴,爲什麼呢,究其原因還是無效計算過多。下面是AC的代碼,主要算法思想寫在註釋裏了,這道題本來很簡單的,卻搞了我這麼久,受打擊了,嗚嗚....

  1. #include <fstream>
  2. using namespace std;
  3. typedef unsigned int UINT;
  4. ifstream fin("kimbits.in");
  5. ofstream fout("kimbits.out");
  6.  
  7. UINT N, L, pos;
  8. UINT dp[33][33]; // dp(i,j)表示長度爲i,1的個數不超過j的串有多少
  9. int res[33]={0};
  10.  
  11. int main()
  12. {
  13.     fin>> N >> L >> pos;
  14.     for(UINT j = 0; j <= L; j++)
  15.         dp[0][j] = 1;
  16.    
  17.     // 方程:f[j,k]=f[j-1,k]+f[j-1,k-1]; 分別表示在當前位加上0和加上1時的兩種狀況
  18.     // 邊界:f[j,0]=1, f[0,j]=1, f[j,k](k>j)=f[j,j]
  19.     // 這樣我們得到了所有的f[j,k] 需要做的就是據此構造出所求字符串
  20.     for(UINT i = 0; i <= N; i++)
  21.     {
  22.         for(UINT j = 0; j <=L; j++)
  23.         {
  24.             if(== 0) dp[i][j] = 1;
  25.             else if(<= i) dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
  26.             else if(> i) dp[i][j] = dp[i][i];
  27.         }
  28.     }
  29.    
  30.     // 構造思路如下:
  31.     // 設所求串爲S,假設S的位中最高位的1在K位
  32.     // 那麼必然滿足:
  33.     // F[K-1,L]<pos and F[K,L]>=pos
  34.     // 這樣的K是唯一的, 所以S的第一個1在從右至左第K位
  35.     // 因爲有F[K-1,L]個串第K位上爲0,所以所求的第I個數的後K位就應該是:
  36.     // 滿足"位數爲K且串中1不超過L-1個"這個條件的第 pos - F[K,L] 個數 => 遞歸的過程
  37.     while(pos > 1)
  38.     {
  39.         for(int i = N-1; i>=0; i--)
  40.         {
  41.             if(dp[i][L] < pos)
  42.             {
  43.                 res[N-i] = 1;
  44.                 pos -= dp[i][L];
  45.                 break;
  46.             }
  47.         }
  48.         L--;
  49.     }
  50.     UINT i = 1;
  51.     while(res[i] == 0 && 33 - i <= N) i++;
  52.     if(== N+1) i = 1;
  53.     for(; i <= N; i++)
  54.         fout << res[i];
  55.     fout << endl;
  56.     return 0;
  57. }



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章