acm博弈论基础总结


常见博弈结论

Nim

问题:共有N堆石子,编号1..n,第i堆中有个a[i]个石子。

每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。

结论:对于一个局面,当且仅当a[1] xor a[2] xor ...xor a[n]=0时,该局面为P局面,即必败局面。

证明:二进制位证明即可。

Moore’s Nim

问题:n堆石子,每次从不超过k堆中取任意多个石子,最后不能取的人失败。

结论:这是一个nim游戏的变形:把n堆石子的石子数用二进制表示,统计每个二进制位上1的个数,若每一位上1的个数mod(k+1)全部为0,则必败,否则必胜。(先手)

证明:分类讨论N/P状态。

Staircase Nim

问题:在阶梯上进行,每层有若干个石子,每次可以选择任意层的任意个石子将其移动到该层的下一层。最后不能操作的人输。

结论:在奇数堆的石子做Nim。

证明:阶梯博弈经过转换可以变为Nim.把所有奇数阶梯看成N堆石子做nim。把石子从奇数堆移动到偶数堆可以理解为拿走石子,就相当于几个奇数堆的石子在做Nim。

New Nim

问题:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。

结论:为使后手必败,先手留给后手的必然是若干线性无关的数字,否则后手可以留下一个异或和为零的非空子集使得先手必败,故问题转化为拿走和最小的数字使得留下的数线性无关,即留下和最大的线性基,这样拿走的数量显然最少,找到和最大的线性基只需贪心的把数字从大到小加入到基中即可.

证明:证明需用到拟阵,但结论的正确性是比较显然的.

Anti-Nim

问题:正常的nim游戏是取走最后一颗的人获胜,而反nim游戏是取走最后一颗的人输。

结论:一个状态为必胜态,当且仅当:

  1)所有堆的石子个数为1,且NIM_sum(xor和)=0

  2)至少有一堆的石子个数大于1,且 NIM_sum(xor和) ≠ 0

Lasker’s Nim

问题:Alice和Bob轮流取石子,每一次可以从任意一堆中拿走任意个石子,也可以将一堆石子分为两个小堆。先拿完者获胜。

结论:if(x%4==0) sg[x]=x-1;if(x%4==1||x%4==2) sg[x]=x;if(x%4==3) sg[x] = x+1。这种问题一般要对sg值打表。(对于先拿完者获胜sg值为零先手必败,否则后手必胜)

Wythoff's game

问题:两堆石子,每次可以取一堆或两堆,从两堆中取得时候个数必须相同,先取完的获胜。

结论: ak =[k *(1+√5)/2],bk= ak + k  (k=0,1,2,…,n 方括号表示取整函数)

证明:

这种情况下是颇为复杂的。我们用(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。

可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk= ak + k,奇异局势有

如下三条性质:

1.任何自然数都包含在一个且仅有一个奇异局势中。

由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。所以性质1。成立。

2.任意操作都可将奇异局势变为非奇异局势。

事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。

3.采用适当的方法,可以将非奇异局势变为奇异局势。

从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。

4.(Betty 定理):如果存在正无理数 A, B 满足 1/A + 1/B = 1,那么集合 P = { [At], t ∈ Z+}、Q = { [Bt], t ∈ Z+} 恰为集合 Z+ 的一个划分,即:P ∪ Q = Z+,P ∩ Q = ø。

5.上述矩阵中每一行第一列的数为 [Φi],第二列的数为 [(Φ + 1)i],其中 Φ = (sqrt(5) + 1) / 2 为黄金分割比。

Bash Game

问题:只有一堆石子共n个。每次从最少取1个,最多取m个,最后取光的人取胜。

结论:如果n=(m+1)*k+s (s!=0) 那么先手一定必胜。

证明:因为第一次取走s个,接下来无论对手怎么取,我们都能保证取到所有(m+1)倍数的点,那么循环下去一定能取到最后一个。

Sprague-Grundy Theorem

Sprague-Grundy Theorem:

SG(x)=mex{SG(y) | y是x的后继}{SG(x):表示当前拿多少为必胜}

g(G)=g(G1)^g(G2)^...^g(Gn)。也就是说,游戏的和的SG函数值是它的所有子游戏的SG函数值的异或。

Fibonacci’s Game

任何正整数可以表示为若干个不连续的Fibonacci数之和。

问题:有一堆个数为n的石子,游戏双方轮流取石子,满足:

1)先手不能在第一次把所有的石子取完;

2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。

结论:先手胜当且仅当n不是Fibonacci数。换句话说,必败态构成Fibonacci数列。

Game On The Tree

树链博弈:

给定一棵 n 个点的树,其中 1 号结点是根,每个结点要么是黑色要么是白色
现在小 Bo 和小 Biao 要进行博弈,他们两轮流操作,每次选择一个黑色的结点将它变白,之后可以选择任意多个(可以不选)该点的祖先(不包含自己),然后将这些点的颜色翻转,不能进行操作的人输
由于小 Bo 猜拳经常输给小 Biao,他想在这个游戏上扳回一城,现在他想问你给定了一个初始局面,是先手必胜还是后手必胜。

题解:每层的黑点数为偶数的时候,为先手必败态。首先,没有黑点,都是0,肯定是先手必败态。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=;
int n,w[maxn],cnt[maxn];
vector<int> g[maxn];
void dfs(int u,int fa,int dep)
{
if(w[u]) cnt[dep]++;
for(int i=,len=g[u].size();i<len;++i)
{
int v=g[u][i];
if(v==fa) continue;
dfs(v,u,dep+);
}
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;++i) scanf("%d",w+i);
for(int i=;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(,,);
bool flag=false;
for(int i=;i<=n;++i)
if(cnt[i]&) flag=true;
if(flag) puts("First");
else puts("Second");
return ;
}

Bamboo Stalks

作为GH游戏的介绍,我们先研究下面的Figure 6.1。n条线段的bamboo stalks游戏是具有n条边的线形图。一步合法的操作是移除任意一条边,玩家轮流进行操作,最后一个进行操作的玩家获胜。n条线段的bamboo stalks游戏能够移动到任意更小线段数(0到n-1)的bamboo stalks游戏局面当中。所以n条线段的bamboo stalks游戏是等同于nim游戏中其中拥有n个石子的一堆。玩一组bamboo stalks游戏就相当于玩nim游戏。

例如,左边的三根竹竿构成的“森林”相当于具有石子数分别为3、4、5三堆石子的nim游戏。就我们所知,3^4^5=2,这是一个能够移动到P局面的N局面,办法是通过取走三根线段的竹竿上的第二根线段,留下一根。而结果变成右边的竹竿分布,而此时的SG值是0,是P局面。

Green Hackenbush on Trees

Colon Principle:当树枝在一个顶点上时,用一个非树枝的杆的长度来替代,相当于他们的n异或之和。

通过bamboo stalks游戏,我们知道GH游戏是换了个形式的nim游戏而已。可是如果我们允许比bamboo stalks游戏更多结构呢?让我们看下在Figure 6.2中由三棵根树组成的森林。根树是一种图,带有一个最高的节点,叫做根,这个根到任意一个其他节点的路径都是独一无二的。实质上就是说这个图不含有圈。

一次合法操作是移除任意一条不与地面相连的线段,此时次线段以上的子树都会被移除。既然这个游戏是公平的,而且根据我们学过的nim游戏,这样的树相当于nim游戏的一些堆,或者说是bamboo stalks游戏(到了这里明白bamboo stalks游戏与nim游戏的单堆是等价的)。这个问题就是寻找每棵树的SG值。

 这里我们要用上一个原理,叫做Colon Principle:当树枝在一个顶点上时,用一个非树枝的杆的长度来替代,相当于他们的n异或之和。

让我们看看这条原理是如何在Figure 6.2中寻找与左树等价的竹竿。这里有两个节点拥有树枝。较高的那个节点拥有两个树枝,每个树枝上有两个节点。1^1=0,所以这两个树枝可以用一个带有0个节点树枝来代替,也就是说可以把这两个树枝给去掉。那就剩下了一个Y形树了,因为同样道理这个Y形树的两个树枝也要被去掉。此时就剩下了一个线段数为1的竹竿游戏模型了。

看Figure 6.3,是Figure 6.2中第二棵树的处理办法,最后可以得到线段数为8的竹竿游戏。第三棵树也可以同样处理,结果是线段数为4的竹竿游戏。(注意,这里所指的竹竿游戏都实质上是nim游戏中的单堆石子)

现在我们可以计算一下图6.2三棵树的sg值,也就是1^8^4=13.既然这个SG值不为0,那么就是一个N局面,先手必胜。问题是要怎么找到胜利方法。很明显这里有一个必胜移动,通过将第二棵树进行操作使得它的SG值为5.可是我们要找哪条边呢?

Figure 6.3的最后一个树长度是8,因为它的前一棵树的三个树枝长度分别是3,2,6,异或值为3^2^6=7,用长度为7的竹竿代替三个树枝后,树的长度就是根加上竹竿长度,即1+7=8。为了最后SG达到5,即使树的长度为5,我们要用长度为4的竹竿来替代那三个树枝。因为2^6=4,所以我们只要将最左边的树枝去掉就行了,当然我们也可以将树枝改动成3^1^6=4.

修剪树的方法用冒号给出了,把所有的树化简为一个单一的竹竿。一个从最高的树枝开始,然后用原理归纳往下到根部。我们现在展示这个原理对于含圈和多重根边的图同样适用。

变形一:边权大于1

LightOJ1355 题目大意:
给一棵带边权的树,两个人分别给边涂色。边权代表了这条边可以涂色的次数。如果一条边的涂色次数没有用完,那么可以涂他子树的边。无法涂色者为负。

题解:green博弈变形,对于都是1的就是green博弈SG[u]^=SG[v]+1;

对于大于1的边,偶数对其没有贡献,奇数有贡献,SG[u]^= SG[v]^(val[v]%2);

 #include<bits/stdc++.h>
using namespace std;
#define RI register int
#define clr(a,val) memset(a,val,sizeof(a))
typedef long long ll;
struct Edge{
int to,val,nxt;
} edge[];
int x,y,z;
int T,n,sum1,sum2,cnt;
int head[],SG[];
inline void addedge(int u,int v,int w)
{
edge[cnt].to=v;
edge[cnt].val=w;
edge[cnt].nxt=head[u];
head[u]=cnt++;
}
inline void dfs(int u,int fa)
{
SG[u]=;
for(int e=head[u];~e;e=edge[e].nxt)
{
int v=edge[e].to;
if(v==fa) continue;
dfs(v,u);
if(edge[e].val==) SG[u]^=(SG[v]+);
else SG[u]^=(SG[v]^(edge[e].val%));
}
}
int main()
{
scanf("%d",&T);
for(RI cas=;cas<=T;++cas)
{
scanf("%d",&n);
clr(head,-); cnt=;
for(RI i=;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);addedge(y,x,z);
}
dfs(,);
if(SG[]) printf("Case %d: Emily\n",cas);
else printf("Case %d: Jolly\n",cas);
}
return ;
}

Green Hackenbush on general rooted graphs

  The Fusion Principle:任何环内的节点可以融合成一点而不会改变图的sg值。(下面我们称它为融合原则)

        融合原则允许我们把任意一个根图简化为一个等效的可以通过冒号原则(即Colon Principle)简化为竹竿的树。

同样,上面的三个图,每个图都相当于nim游戏的一个堆,三个图组成了一个nim游戏。接下来我们要找到这些图等价的nim堆,方便我们解决问题。这要用到融合原则。我们可以把两个相邻的节点合成一个节点,并把它们之间的边弯曲,变成一个圈。一个圈是把自己作为边的另一端的一种边。比如Figure 6.5的最右边那个杂戏表演者的头就是一个圈。在GH游戏中,一个圈是可以被一个叶子(一条没有任何树枝与它相连的边)所代替的,见Figure 6.6中第三幅图到第四幅图的转化。

  The Fusion Principle:任何环内的节点可以融合成一点而不会改变图的sg值。(下面我们称它为融合原则)

        融合原则允许我们把任意一个根图简化为一个等效的可以通过冒号原则(即Colon Principle)简化为竹竿的树。

如下图左部分所示的一个门,在地板上的两个节点是同样的节点来的(记住地板相当于一个单独的节点),所以实际上是一个有一个节点与地板相连的三角形,即第二幅图。融合原则告诉我们,这相当于一个单独的节点有三个圈与它相连。所以造就了第三幅到第四幅的转变,过程是把任意两点收缩成一个圈,3个点两两收缩便可得到三个圈。每个圈又相当于长度为1的竹竿,它们的异或和还是长度为1的竹竿。

我们会发现,拥有奇数条边的环可简化为一条边,偶数条边的环可简化为一个节点。例如,在Figure 6.5中的第二幅图圣诞树中的有四条边的环,会缩减成一个节点,所以这圣诞树最后会简化为一个长度为1的竹竿。相似的,房子上的烟囱变成一个单独的节点,右边的窗户变成一个点,继续下去,就可以看出房子的SG值为3。

SG函数的一些题目

LightOJ1199

题意:有n堆石子(1<=n<=100),每一堆分别有xi个石子(1<=xi<=10000), 一次操作可以使一堆石子变成两堆数目不相等的石子, 最后不能操作的算输,问先手胜还是后手胜。

对于每一个数下一个拆分为 (1,n-1),(2,n-2) ...  个.SG函数推到即可;

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=;
int T,n,SG[maxn],x,ans,vis[maxn];
void getSG(int n)
{
for(int i=;i<=n;++i)
{
memset(vis,,sizeof vis);
for(int j=;j*<i;++j) if(i!=j*) vis[SG[j]^SG[i-j]]=;
for(int j=;j<maxn;++j){ if(!vis[j]) { SG[i]=j;break; } }
}
}
int main()
{
scanf("%d",&T);
getSG(maxn);
for(int cas=;cas<=T;++cas)
{
scanf("%d",&n);ans=;
for(int i=;i<=n;++i)
{
scanf("%d",&x);
ans^=SG[x];
}
if(ans) printf("Case %d: Alice\n",cas);
else printf("Case %d: Bob\n",cas);
}
return ;
}

LightOJ1229

题目意思就是给你一行字符串由 '.'和'X'组成,然后两个人交替将一个‘.’

变成'X'.如果某个人先形成连续的3个‘X’,则这个人就取得胜利。问先手必胜的位置是否存在,

如果存在,有多少个,并依次输出其位置;这题肯定是枚举每一个位置,判断是否可以胜利,

对于SG[x]表示长度为x的‘.’区间的SG值,然后对于每一个位置(不是'X'的位置),判断其由

‘.’变成'X'之后是否可以形成连续3个'X'的必胜状态,是否会形成.XX或XX.或X.X的必败状态;如果都不是再去枚举每一个区间的SG值,再将其异或ans,就得到这一个位置的SG值,为0必胜,不为零必败;

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll;
#define clr(a,val) memset(a,val,sizeof(a))
const int maxn=;
int T,SG[maxn],len;
vector<int> v;
char s[maxn],s1[maxn];
int getSG(int m)
{
if(m<) return ;
if(SG[m]!=-) return SG[m];
bool vis[maxn];clr(vis,);
for(int i=;i<=m;++i) vis[getSG(i-)^getSG(m-i-)]=;
int t=;
while(vis[t]) ++t;
return SG[m]=t;
} bool check(int x)
{
strcpy(s1,s);
if(s1[x]=='X') return ;
s1[x]='X';
for(int i=;i<len-;++i) {if(s1[i]=='X'&&s1[i+]=='X'&&s1[i+]=='X') return ;}
for(int i=;i<len-;++i) {if(s1[i]=='X'&&s1[i+]=='X') return ;}
for(int i=;i<len-;++i) {if(s1[i]=='X'&&s1[i+]=='X') return ;}
int j=-,f=,ans=;
for(int i=;i<len;++i)
{
if(s1[i]=='X')
{
if(f) ans^=getSG(i-j-);
else ans^=getSG(i-j-),f=;
j=i;
}
}
ans^=getSG(len-j-);
return ans==;
} int main()
{
scanf("%d",&T);
memset(SG,-,sizeof SG);
for(int cas=;cas<=T;++cas)
{
scanf("%s",s);
v.clear();
len=strlen(s);
for(int i=;i<len;++i)
{
if(check(i)) v.push_back(i+);
}
printf("Case %d:",cas);
if(v.size())
{
for(int i=;i<v.size();++i) printf(" %d",v[i]);
puts("");
}
else printf(" 0\n");
} return ;
}

LightOJ1344

现在有几串手镯,每个手镯上都有一些珠子,每个珠子都有权值,Aladdin 和 Genie 轮流取珠子,当取了一颗珠子后,这个手镯上所有权值大于等于这颗珠子权值的珠子,都要被删去。因此,这个手镯就会变成新的几个手镯(因为被切割了)。

如,5-1-7-2-4-5-3 这串手镯,选第一颗珠子,权值为5,因此,5,7,5 就要被删去。手镯变成了新的 1,2-4,3 三个手镯。 最后谁不能拿,谁输;

题解:每个手镯都是独立的,因此可以异或每个手镯的 sg 值求解。而每个手镯,可以在某些结点被取走珠子,变成新的几段,任意一种情况的 sg 值,便是新分成的几段的 sg 值异或起来。再将每一种情况的 sg 值,记录在 vis 中,查找没出现过的最小的值,便是这个手镯的 sg 值。

 #include<bits/stdc++.h>
using namespace std;
const int maxn = ;
int sg[maxn][maxn];
int arr[maxn][maxn], num[maxn];
int sgtmp[maxn];
int ret[maxn][maxn]; struct node {
int x, y;
} output[maxn * maxn];
bool operator==(node a,node b){return a.x==b.x&&arr[a.x][a.y]==arr[b.x][b.y];}
bool cmp(node a,node b)
{
if(a.x==b.x) return arr[a.x][a.y]<arr[b.x][b.y];
return a.x<b.x;
} int dfs(int now,int l,int r)
{
if(l>r) return ;
if(l==r) return sg[l][r]=;
if(sg[l][r]!=-) return sg[l][r]; int vis[maxn];
memset(vis,,sizeof(vis)); for(int i=l;i<=r;++i)
{
int tmp=,last=-; for(int j=l;j<=r;++j)
{
if(arr[now][j]<arr[now][i])
{
last = j;
break;
}
}
for(int j = last + ; j <= r && last != -; ++j)
{
if(arr[now][j] >= arr[now][i])
{
tmp ^= dfs(now, last, j - );
last = -;
for (int k = j + ; k <= r; ++k)
{
if (arr[now][k] < arr[now][i])
{
last = j = k;
break;
}
}
}
}
if(last != -) tmp ^= dfs(now, last, r);
vis[tmp] = ;
if (l == && r == num[now]) ret[now][i] = tmp;
}
for(int i = ;;++i)
{
if(vis[i]==)
{
sg[l][r]=i;
return i;
}
}
}
int main()
{
int t, k, ca = ;
scanf("%d", &t);
while (t--)
{
int ans = ;
scanf("%d", &k);
for(int i = ; i <= k; ++i)
{
memset(sg, -, sizeof(sg));
scanf("%d", &num[i]);
for(int j = ; j <= num[i]; ++j) scanf("%d", &arr[i][j]);
sgtmp[i] = dfs(i, , num[i]);
ans ^= sgtmp[i];
}
if (ans == ) printf("Case %d: Genie\n", ca++);
else
{
int cnt = ;
printf("Case %d: Aladdin\n", ca++);
memset(output, , sizeof(output));
for (int i = ; i <= k; ++i)
for (int j=;j<=num[i];++j){if((ans^sgtmp[i]^ret[i][j])==) output[cnt++] = {i, j};}
sort(output,output+cnt,cmp);
cnt=(int)(unique(output,output+cnt)-output);
for(int i = ; i < cnt; ++i)
printf("(%d %d)", output[i].x, arr[output[i].x][output[i].y]);
printf("\n");
}
}
return ;
}

acm博弈论基础总结的更多相关文章

  1. ACM博弈论基础

    博弈论的题目有如下特点: 有两名选手 两名选手交替操作,每次一步,每步都在有限的合法集合中选取一种进行 在任何情况下,合法操作只取决于情况本身,与选手无关 游戏败北的条件为:当某位选手需要进行操作时, ...

  2. ACM&OI 基础数论算法专题

    ACM&OI 基础数学算法专题 一.数论基础 质数及其判法 (已完结) 质数的两种筛法 (已完结) 算数基本定理与质因数分解 (已完结) 约数与整除 (已完结) 整除分块 (已完结) 最大公约 ...

  3. 博弈论基础之sg函数与nim

    在算法竞赛中,博弈论题目往往是以icg.通俗的说就是两人交替操作,每步都各自合法,合法性与选手无关,只与游戏有关.往往我们需要求解在某一个游戏或几个游戏中的某个状态下,先手或后手谁会胜利的问题.就比如 ...

  4. 博弈论基础知识: 巴什博奕+斐波那契博弈+威佐夫博奕+尼姆博弈(及Staircase)(转)

    (一)巴什博奕(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个.最后取光者得胜.若(m+1) | n,则先手必败,否则先手必胜.显然,如果n=m+1 ...

  5. ACM博弈论总结

    一.Bash博弈 1.问题模型:只有一堆n个物品,两人轮流从这堆物品中取物,最多取m个,最后取光者胜. 2.解决思路:当n=m+1时,由于一次最多取m个,无论先取者拿走多少个,后取者都能一次拿走剩余的 ...

  6. 博弈论揭示了深度学习的未来(译自:Game Theory Reveals the Future of Deep Learning)

    Game Theory Reveals the Future of Deep Learning Carlos E. Perez Deep Learning Patterns, Methodology ...

  7. 第六届acm省赛总结(退役贴)

    前言: 这是我的退役贴,之前发到了空间里,突然想到也要在博客里发一篇,虽然我很弱,但是要离开了还是有些感触,写出来和大家分享一下,希望不要见笑.回来看看,这里也好久没有更新了,这一年确实有些懈怠,解题 ...

  8. 关于ACM,关于CSU

    原文地址:http://tieba.baidu.com/p/2432943599 前言: 即将进入研二,ACM的事情也渐渐远去,记忆终将模糊,但那段奋斗永远让人热血沸腾.开个贴讲讲ACM与中南的故事, ...

  9. 20181228 模拟赛 T3 字符串游戏 strGame 博弈论 字符串

    3  字符串游戏(strGame.c/cpp/pas) 3.1  题目描述 pure 和 dirty 决定玩 T 局游戏.对于每一局游戏,有n个字符串,并且每一局游戏由K轮组成.具体规则如下:在每一轮 ...

随机推荐

  1. PHP更新用户微信信息的方法

    PHP更新用户微信信息的方法 大家都知道 授权登录一次 获取后 再登录就会提示已经授权登录 就没办法重新获得用户信息了 这个时候根据openid来获取了请求user/info这个获取ps:必须关注过公 ...

  2. python 基础之 模块

    Python 基础之模块 一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 就是一个python文件中定义好了类和方法,实现了一些功能,可以被别的python文 ...

  3. (数据科学学习手札71)在Python中制作个性化词云图

    本文对应脚本及数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 一.简介 词云图是文本挖掘中用来表征词频的数据可视化 ...

  4. Maven和Gradle中配置单元测试框架Spock

    Maven Maven本身不支持其他JVM语言(例如Groovy或Scala).要在Maven项目中使用它,需要使用第三方插件.对于Groovy而言,最好的选择似乎是GMavenPlus(重写不再维护 ...

  5. SpEL + AOP实现注解的动态赋值

    一.自定义注解 先聊聊这个需求,我需要根据用户的权限对数据进行一些处理,但是痛点在哪里呢?用户的权限是在请求的时候知道的,我怎么把用户的权限传递给处理规则呢?想了以下几种方案: Mybatis 拦截器 ...

  6. pat 1035 Password(20 分)

    1035 Password(20 分) To prepare for PAT, the judge sometimes has to generate random passwords for the ...

  7. 【Java】面向对象之多态

    生活中,比如动物中跑的动作,小猫.小狗和大象,跑起来是不一样的.再比如飞的动作,昆虫.鸟类和飞机,飞起来也是不一样的.可见,同一类的事物通过不同的实际对象可以体现出来的不同的形态.多态,描述的就是这样 ...

  8. VLAN实验(1)Access接口

    1.选择两台S3700的交换机,5台PC机,并按照下图链接好并填写IP,完成此拓扑图 2.由于现在我们还没有划分VLAN,这5台PC,还在同一个VLAN中,现在我们启动所有的设备,这是所有的主机应该是 ...

  9. webapi接口安全验证

    其实跟大多数网上的方法一样,在前端请求头里加token,后台通过拦截器处理token数据,然后两边对比,如果一样就能通过,不一样就返回无权限. 前端测试代码如下: @{ ViewBag.Title = ...

  10. 和SharpDX坑爹的Variant刚正面

    和SharpDX坑爹的Variant刚正面 几个月前我写了和篇文章<.NET中生成动态验证码>文章,其实里面藏着一个大坑.运行里面的代码,会发现运行的gif图片并没有循环播放: 细心的网友 ...