【CodeVS】1245 最小的N個和 - Ⅰ - 原題的幾種解法
【題意】有兩個長度為
【資料範圍】
【思路1】30分做法
直接把
時間複雜度:
空間複雜度:
【思路2】AC做法
求前N小,涉及到單調性,試著排序使得A,B兩個序列從小到大。
我們從1到N依次找到前N小的和,那麼每次就要從決策中選出一個最小的元素,現在要求每次決策考慮的元素個數儘可能少。
可以按行把所有的
第1行:
第2行:
第i行:
第n行:
對於第i行,若
那麼,在決策的時候只用考慮每一行當前未選的最前一個。
即:設l
至於每次的決策,用堆可以做到
時間複雜度:
空間複雜度:
程式碼:實測136ms
#include <cstdio>
#include <cctype>
#include <queue>
#include <algorithm>
using namespace std;
const int N=161240;
int n;
int A[N],B[N];
int lev[N];
inline int Read(void)
{
int s=0,f=1; char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) s=s*10+c-'0';
return s;
}
struct cmp
{
inline int operator () (int i,int j)
{
return A[i]+B[lev[i]]>A[j]+B[lev[j]];
}
};
priority_queue<int,vector<int>,cmp> q;
int main(void)
{
n=Read();
for (int i=1;i<=n;i++) A[i]=Read();
for (int i=1;i<=n;i++) B[i]=Read();
sort(A+1,A+n+1);
sort(B+1,B+n+1);
fill(lev+1,lev+n+1,1);
for (int i=1;i<=n;i++) q.push(i);
int now;
for (int i=1;i<=n;i++)
{
now=q.top(),q.pop();
printf("%d ",A[now]+B[lev[now]++]);
q.push(now);
}
printf("\n");
return 0;
}
【思路3】AC做法
首先排序,然後還是用【思路2】的思路,從1到N依次求前N小。
注意到【思路2】中我們只使用了橫向的分類,而沒有考慮縱向的分類,這是可以有一些決策狀態排除掉的。
於是我們想到直接把這些狀態當作一個二維的圖來看:
B\A | 1 | 2 | … | i | … | n |
---|---|---|---|---|---|---|
1 | ||||||
2 | ||||||
… | ||||||
i | ||||||
… | ||||||
n |
其中
我們變為了二維的擴充套件,即在當前狀態中選擇一個最小的,然後向下、向右擴充套件決策狀態。
這裡需要注意的是,有些格子是不用加入待決策狀態中的:它的左邊或上邊還沒有決策出來。
這樣有一定的優化作用。
時間複雜度:
空間複雜度:
程式碼:實測88ms
#include <cstdio>
#include <cctype>
#include <queue>
#include <algorithm>
using namespace std;
#define mp(i,j) make_pair(i,j)
#define fs first
#define sc second
typedef pair<int,int> PairInt;
const int N=161240;
int n;
int A[N],B[N];
int cross[N],line[N];
inline int Read(void)
{
int s=0,f=1; char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) s=s*10+c-'0';
return s;
}
struct cmp
{
inline int operator () (PairInt Pi,PairInt Pj)
{
return A[Pi.fs]+B[Pi.sc]>A[Pj.fs]+B[Pj.sc];
}
};
priority_queue<PairInt,vector<PairInt>,cmp> q;
inline int Check(int i,int j)
{
if (cross[i]+1<j) return 0;
if (line[j]+1<i) return 0;
return 1;
}
int main(void)
{
n=Read();
for (int i=1;i<=n;i++) A[i]=Read();
for (int i=1;i<=n;i++) B[i]=Read();
sort(A+1,A+n+1);
sort(B+1,B+n+1);
PairInt now;
q.push(mp(1,1));
for (int i=1;i<=n;i++)
{
now=q.top(),q.pop();
printf("%d ",A[now.fs]+B[now.sc]);
cross[now.fs]=now.sc;
line[now.sc]=now.fs;
if (Check(now.fs+1,now.sc)) q.push(mp(now.fs+1,now.sc));
if (Check(now.fs,now.sc+1)) q.push(mp(now.fs,now.sc+1));
}
printf("\n");
return 0;
}
【思路4】AC做法
按照【思路1】,我們把所有可能的情況都算出來,然後排序。
然而【思路1】的時間和空間都是我們承受不了的,怎麼辦?
想辦法把一些一定不可能的狀態給消除掉。
首先還是給A,B排序,同樣還是這個表:
B\A | 1 | 2 | … | i | … | n |
---|---|---|---|---|---|---|
1 | ||||||
2 | ||||||
… | ||||||
i | ||||||
… | ||||||
n |
觀察到,對於
由於我們要求前N小的,所以滿足要求的點至少要滿足
這樣我們可以把點的個數縮小至
時間複雜度:
空間複雜度:
程式碼:實測172ms
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=161240;
const int S=3000000;
int n;
int A[N],B[N];
int t[S],len;
inline int Read(void)
{
int s=0,f=1; char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) s=s*10+c-'0';
return s;
}
int main(void)
{
n=Read();
for (int i=1;i<=n;i++) A[i]=Read();
for (int i=1;i<=n;i++) B[i]=Read();
sort(A+1,A+n+1);
sort(B+1,B+n+1);
for (int i=1;i<=n;i++)
for (int j=1;i*j<=n;j++) t[++len]=A[i]+B[j];
sort(t+1,t+len+1);
for (int i=1;i<=n;i++) printf("%d ",t[i]);
printf("\n");
return 0;
}
【小結】
對於無序的集合,通常要將它定序,常見的定序方法就是從小到大或者從大到小。
求第K小的方法通常有以下幾種:
①依次求
②排序可能情況
③二分答案對於方法①“依次求”,每次在待定狀態內的元素要儘可能少,可以通過某些性質來減少元素的個數。
通常的做法是構建多條元素的單調序列,滿足先選完前一個再選後一個,這樣用優先佇列甚至不用(例如《醜數》一題)即可。
這種做法甚至可以擴充套件到二維單調性,然後在平面上擴充套件。對於方法②“排序可能情況”,待定情況要儘可能的少,這要通過某些性質來排除一些不可能的情況。
例如本題只限定在i×j≤n i\times j\leq n內求,最後弄出了調和級數,總共的情況數為O(nlogn) O(n log n)。對於方法③,在【變式】會提及。
方法的比較
方法①,方法②處理範圍較小,詢問較多的問題;
方法③處理範圍較大,詢問較少的問題。
相關文章
- RMQ問題的各種解法MQ
- 【小 w 的代數】(提供一種 n^2 log 的解法)
- 最小可用Id的命令式解法
- 力扣.1 兩數之和 N 種解法 two-sum力扣
- codevs 1213 解的個數dev
- Leetcode 通過率最高的困難題 N皇后 II 【回溯解法-剪枝】LeetCode
- 字首和的n個神奇操作
- 幾數之和分析,解法,優化和總結優化
- LeetCode 連結串列專題 19 刪除倒數第n個數 java 遞迴解法 幾乎雙百 思路簡單LeetCodeJava遞迴
- 杭電OJ 2028求n個數的最小公倍數
- 演算法(第4版)練習題1.1.27的三種解法演算法
- 百度的一道 java 高頻面試題的多種解法Java面試題
- 關於求解不定方程的n(n-1)=2m(m-1)的解法的總結
- [每日一題] 第二十題:最小的k個數每日一題
- 3516 求n個整數的最小值 迴圈結構
- 尋找陣列中的最大值和最小值O(1.5*N)陣列
- 最近的幾個技術問題總結和答疑
- 陣列的最小不可組成和問題陣列
- 兩種解法搞定Swap Nodes in Pairs演算法題AI演算法
- iOS APNs的幾個問題iOS
- 最小化醫療保健中應用程式威脅風險的幾種方法
- JavaScript的two-sum問題解法JavaScript
- N皇后問題(各種優化)優化
- TQM的八項原則,你知道幾個?
- Web前端主題切換的幾種方案Web前端
- 最近的幾個技術問題總結和答疑(九)
- 最近的幾個技術問題總結和答疑 (11)
- 最近的幾個技術問題總結和答疑(七)
- 最近的幾個技術問題總結和答疑(八)
- 最近的幾個技術問題總結和答疑(二)
- 最近的幾個技術問題總結和答疑(三)
- 最近的幾個技術問題總結和答疑(四)
- 最近的幾個技術問題總結和答疑(六)
- 最近的幾個技術問題總結和答疑(五)
- css 實現豎直居中的 N 種場景及 N 種方法CSS
- POJ 2442-Sequence(優先佇列-m組n個數每組取一個求n個最小值)佇列
- [每日一題] 第十六題:n個骰子的點數每日一題
- 3069 求n個整數的和