3.23
字典树、拓扑排序、并查集
字典序的第K小数字
thinking
由题设,我们可以知道,第k小的数字,就是前序遍历字典树的第k个数字,
-
1
-
10 11 12
- 101 102
……..
-
问题就是如何遍历此字典树:
容易得到,对于节点i来说,它的子节点有10xi+1——10xi+9。
我们用1-n对所有的节点进行标号统计
- 假设我们查询到第i个节点,那么我们还需要查询\(k-i\)个节点,我们不妨设i的子树有\(count_i\)个节点,
- if \(count_i\)<=\(k-i\),那么我们需要到相邻的子树上进行查询
- if \(count_i\)>\(k-i\),那么需要的节点就是在当前的子树上,检查下一层节点。
- 继续调用上述的步骤
那么,我们目前需要解决的就是,\(count_i\)的求解,\(left=i*10,right=i*10+9\),所以下一层有\(right-left+1\)个节点,同理,继续更新\(left,right\),其中\(right=min(right*10+9,n)\),截至条件为\(left<n\)。
solution
class Solution {
public:
int getcount(int ans,int n) {
int count=0;
long left=ans,right=ans;
while(left<=n) {
count+=min(right,(long)n)-left+1;
left=left*10;
right=right*10+9;
}
return count;
}
int findKthNumber(int n, int k) {
int ans=1;
--k;
while(k>0) {
int count=getcount(ans,n);
if(count<=k) {
k-=count;
++ans;
} else {
ans*=10;
--k;
}
}
return ans;
}
};
奇怪的打印机 II
thinking
我们直接从题设进行考虑,让我们求解是否有一种颜色的序列,满足先后涂抹后,可以使得举行变成给定的样子——即对颜色进行拓扑排序。
图相关的题目感觉代码量就是大。
由于题目提供数据,我们首先确定各种颜色的上下左右边界的范围大小,如果说,某一种颜色在另一种颜色的范围内,那么此种颜色相当于包围它的那种颜色来说,是排在后面的,我们建立一条有向的边。最后对建成的图进行拓扑排序,查找是否出现环即可。下面即是代码实现。
solution
class Solution {
public:
int w[61],a[61],s[61],d[61];
bool isPrintable(vector<vector<int>>& t) {
int m=t.size();
int n=t[0].size();
int k;
memset(w,127,sizeof(w));
memset(a,127,sizeof(a));
memset(s,0,sizeof(s));
memset(d,0,sizeof(d));
for(int i=0;i<m;++i)
for(int j=0;j<n;++j) {
k=t[i][j];
w[k]=min(w[k],i);
a[k]=min(a[k],j);
s[k]=max(s[k],i);
d[k]=max(d[k],j);
}
bool vis[61][61]={0};//判断是否有边相连
vector<vector<int>> edge(61);
int in[61];//统计入度
memset(in,0,sizeof(in));
for(int i=0;i<m;++i)
for(int j=0;j<n;++j) {
k=t[i][j];
for(int color=1;color<=60;++color) {
if(w[color]<=i&&a[color]<=j&&s[color]>=i&&d[color]>=j&&color!=k&&!vis[color][k]) {
edge[color].push_back(k);
++in[k];
vis[color][k]=true;
}
}
}
int i;
vector<int> v;
while(1) {
for(i=1;i<=60;++i) {
if(in[i]==0) {
v.push_back(i);
in[i]=-1;
for(int e:edge[i]) {
--in[e];
}
break;
}
}
if(i==61) break;
}
return v.size()==60;
}
};
Satisfiability of Equality Equations
thinking
并查集板子题,如果是false,必然存在某一个或者多个\(x_i=y_i\),\(x_i!=y_i\)同时存在,我们需要对等号进行合并,再非等号上查询即可。
代码如下:
solution
class UnionFind
{
public:
vector<int> par;
vector<int> rank;
public:
void init(int n)
{
par.reserve(n);
rank.reserve(n);
for(int i=0;i<n;i++)
{
par[i]=i;
rank[i]=i;
}
}
int find(int x)
{
if(par[x]==x) {return x;}
else {
return par[x]=find(par[x]);
}
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y) return;
if(rank[x]<rank[y]) {par[x]=y;
} else {
par[y]=x;
if(rank[x]==rank[y]) rank[x]++;
}
}
bool same(int x,int y)
{
return find(x)==find(y);
}
};
class Solution {
public:
bool equationsPossible(vector<string>& a) {
UnionFind uf1,uf2;
uf1.init(26),uf2.init(26);
for(auto &&ee:a) {
if(ee[1]=='=') {
uf1.unite(ee[0]-'a',ee[3]-'a');
}
}
for(auto &&ee:a) {
if(ee[1]=='!') {
if(uf1.same(ee[0]-'a',ee[3]-'a')) return false;
uf2.unite(ee[0]-'a',ee[3]-'a');
}
}
return true;
}
};

浙公网安备 33010602011771号