3.16
博弈,并查集,哈希表,双向链表
除数博弈
thinking
我们不妨选取几个例子来观察这个博弈的问题:(为方便,我们将站在Alice的角度进行选取数字)
- n=1,false
- n=2,选取1,true
- n=3,选1,参考2,false
- n=4,选1,2,参考2或者3的选择,我们选1,true
- n=5,选取1,参考4的情况,false
我们不妨大胆地猜测,认为偶数必胜,奇数必输,证明如下:
我们知道对于任何的n,1均为可选,同时,我们不选择x为1的时候,这一次操作同样可以用k次减一来进行模拟。因此,我们可以直接认为每一次操作减一,易得a拿到2时,必胜。如果a碰到偶数,那么b一定是奇数,再一次后,a仍然是偶数,直到2为止a获胜。同理,a是奇数,则一定会失败。
solution
class Solution {
public:
bool divisorGame(int n) {
return (n&1)==0;
}
};
获取生成数组中的最大值
thinking
简单模拟
solution
class Solution {
public:
int a[102];
int getMaximumGenerated(int n) {
if(n==0) return 0;
int ans=1;
memset(a,0,sizeof(a));
a[1]=1;
for(int i=2;i<=n;++i) {
if((i&1)==1) {
a[i]=a[(i-1)/2]+a[(i-1)/2+1];
}else {
a[i]=a[i/2];
}
ans=max(ans,a[i]);
}
return ans;
}
};
传递信息
thinking
看一眼数据量,直接爆搜有没有
solution
class Solution {
public:
int ans=0;
void dfs(int k,int pos,vector<vector<int>> &e,int n) {
if(k==0) {
if(pos==n-1) ++ans;
return;
}
int len=e[pos].size();
for(int i=0;i<len;++i) {
--k;
dfs(k,e[pos][i],e,n);
++k;
}
}
int numWays(int n, vector<vector<int>>& relation, int k) {
vector<vector<int>> e(n);
for(auto &r:relation) e[r[0]].push_back(r[1]);
dfs(k,0,e,n);
return ans;
}
};
冗余连接
thinking
并查集经典题,判断删除一个边后,整个图是否连通,并输出最后面的边
solution
class UnionFind {
public:
vector<int> parent;//父辈节点
vector<int> size;//当前的层数
int n;
int setCount;//当前的连通分量
public:
UnionFind(int _n):n(_n),setCount(_n),parent(_n),size(_n,1) {
iota(parent.begin(),parent.end(),0);
}
int findset(int x) {return parent[x]==x?x:parent[x]=findset(parent[x]);}
bool unite(int x,int y) {
x=findset(x);y=findset(y);
if(x==y) return false;
if(size[x]<size[y]) swap(x,y);
parent[y]=x;
size[x]+=size[y];
--setCount;
return true;
}
bool connected(int x,int y) {return findset(y)==findset(x);}
};
class Solution {
public:
vector<int> findRedundantConnection(vector<vector<int>>& e) {
int n=e.size();
vector<int> ans(2);
for(int i=0;i<n;++i) {
UnionFind uf(n+1);
for(int j=0;j<n;++j) {
if(i==j) continue;
uf.unite(e[j][0],e[j][1]);
}
if(uf.setCount==2) ans=e[i];
}
return ans;
}
};
全 O(1) 的数据结构
thinking
考察数据结构的运用,可以先做下面的题对这一题进行铺垫,有一点点的小印象。
对于本题,我们采用双向链表+哈希表,维护双向链表正序,维护字符串的次数,使用哈希表存下节点的地址,方便查找。具体如下:
solution
struct Link {//双向链表
string key;
int cnt;
Link* pre;
Link* next;
Link(string s,int c):key(s),cnt(c),pre(nullptr),next(nullptr) {}
};
const int inf=0x3f3f3f3f;
class AllOne {
public:
//link 呈现单调递减
unordered_map<string,Link*>hash;
Link* head;
Link* tail;
AllOne() {
//哑节点
head=new Link("",inf);
tail=new Link("",-1);
head->next=tail;
tail->pre=head;
}
void inc(string key) {
if(!hash.count(key)) {
//找不到这个节点
Link* node=new Link(key,1);
hash[key]=node;
node->next=tail;
node->pre=tail->pre;
node->pre->next=node;
node->next->pre=node;
} else {
//找到了这个节点
Link* node=hash[key];
node->cnt=(node->cnt)+1;
while(node->cnt>node->pre->cnt) {
swap(node->pre,node);
}
}
}
void dec(string key) {
Link* node=hash[key];
node->cnt=(node->cnt)-1;
if(node->cnt==0) {
//需要删除该节点
node->next->pre=node->pre;
node->pre->next=node->next;
delete node;
hash.erase(key);
} else {
//需要移动该节点
while(node->cnt<node->next->cnt) {
swap(node,node->next);
}
}
}
string getMaxKey() {
if(head->next->cnt!=-1) return head->next->key;
return "";
}
string getMinKey() {
if(tail->pre->cnt!=inf) return tail->pre->key;
return "";
}
void swap(Link* left,Link* right) {//交换两个节点
left->next=right->next;
right->pre=left->pre;
left->pre=right;
right->pre->next=right;
right->next=left;
left->next->pre=left;
}
};
LRU 缓存(medium)
thinking
我们采用哈希表,与双向链表进行操作。详细原理如下:
我们储存每一对键值对\((key,value)\),用双端链表进行维护查询数据的新旧,即是,我们将每一次刚访问过或者是刚插入的数据移动到链表的头部,当数据量超出容量的时候,我们只需要删除来年表最后一个节点即可。具体代码如下:
solution
struct DlinkedNode {
int key,value;
DlinkedNode* prev;
DlinkedNode* next;
DlinkedNode():key(0),value(0),prev(nullptr),next(nullptr) {}
DlinkedNode(int _key,int _value):key(_key),value(_value),prev(nullptr),next(nullptr) {}
};//双向链表的结构体
//不过结构体里面也能定义函数???
class LRUCache {
private:
unordered_map<int,DlinkedNode*>cache;
DlinkedNode* head;
DlinkedNode* tail;
int size;
int capacity;
public:
LRUCache(int _capacity) {//初始化
capacity=_capacity;
size=0;
head=new DlinkedNode();
tail=new DlinkedNode();
head->next=tail;
tail->prev=head;//设置前后哑节点,方便后续操作
}
int get(int key) {
if(!cache.count(key)) return -1;//查找key值
DlinkedNode* node=cache[key];;
moveToHead(node);
return node->value;
}
void put(int key, int value) {
if(!cache.count(key)) {//查不到key值,则手工添加key值
DlinkedNode* node=new DlinkedNode(key,value);
cache[key]=node;
addToHead(node);
++size;
if(size>capacity) {//size++,判断一下,是否需要移除
DlinkedNode *removed=removeTail();
cache.erase(removed->key);
delete removed;
--size;
}
} else {//查到key值,更新value即可
DlinkedNode* node=cache[key];
node->value=value;
moveToHead(node);
}
}
void addToHead(DlinkedNode* node) {//将独立的node移动到头部
node->prev=head;
node->next=head->next;
head->next->prev=node;
head->next=node;
}
void removeNode(DlinkedNode* node) {//移除node节点
node->prev->next=node->next;
node->next->prev=node->prev;
}
void moveToHead(DlinkedNode* node) {//将节点从链表中移动到头部
removeNode(node);
addToHead(node);
}
DlinkedNode* removeTail() {//删除并返回节点
DlinkedNode* node=tail->prev;
removeNode(node);
return node;
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/

浙公网安备 33010602011771号