博弈论的算法总结
开头先啰嗦一句:想学好博弈,必然要花费很多的时间,深入学习,不要存在一知半解,应该是一看到题目,就想到博弈的类型。
以及,想不断重复不断重复,做大量各大oj网站的题目,最后吃透它。
博弈:
博弈论又被称为对策论(Game Theory),既是现代数学的一个新分支,也是运筹学的一个重要学科。
博弈,具体的例子就是下棋,双方都考虑最有利于自已的步骤,但是最终必有一方输,一方赢。
博弈的策略:参与者在行动之前所准备好的一套完整的行动方案,就是想好下完这步棋,对方会如何下,
以及接下来该如何下,最终得出结果。
常见的博弈有以下:
1.博弈:合作博弈和非合作博弈
合作博弈:指参与者能够达成一种具有约束力的协议,在协议范围内选择有利于双方的策略
非合作博弈:指参与者无法达成这样一种协议
2.博弈:静态博弈和动态博弈
静态博弈:指在博弈中,参与者同时选择,或虽非同时选择,但是在逻辑时间
上是同时的。(期末老师评分与同学给老师评分)
动态博弈:指在博弈中,参与者的行动有先后顺序,且后行动者能够观察
到先行动者的行动。(下棋)
3.博弈:完全信息博弈与不完全信息博弈
完全信息博弈:指在博弈中,每个参与者对其他参与者的类型,策略空间及损益函数都有准确的信息。(卖家与买家)
不完全信息博弈:总有一些信息不是所有参与者都知道的
4.博弈:零和博弈与非零和博弈
零和博弈:指博弈前的损益总和与博弈后的损益总和相等
非零和博弈:指博弈后的损益大于(小于)博弈前的损益总和(正和或负和 )
下面我主要讲一些关于算法比赛中用到的博弈类型:
首先你要理解必胜状态和必败状态:
对下先手来说,
一个状态是必败状态当且仅当它的所有后继都是必败状态。
一个状态是必胜状态当且仅当它至少有一个后继是必败状态。
就是说,博弈者,一旦捉住了胜利的把柄,必然最后胜利。
博弈中常常用到的:
两个数,不用中间变量实现交换。
a b;
a = a^b;
b = a^b;
a = a^b;
巴什博弈:
百度百科:
巴什博弈:只有一堆n个物品,两个人轮流从这堆物品中取物, 规定每次至少取一个,最多取m个。最后取光者得胜。
显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。对于巴什博弈,那么我们规定,如果最后取光者输,那么又会如何呢?(n-1)%(m+1)==0则后手胜利
请去刷下面的题目,均是巴什博弈
算博弈题目时,一定要算到一个周期结束,防止出错,很有可能像HDU2897那样。中途错的猝不及防
package Combat.com; import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String []args) { Scanner cin = new Scanner(System.in); while(cin.hasNext()) { int n = cin.nextInt(); PLG(n); } } static void PLG(int n) { if(n%3 == 0) { System.out.println("Cici"); } else { System.out.println("Kiki"); } } }
import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String []args) { Scanner cin = new Scanner(System.in); while(cin.hasNext()) { int n = cin.nextInt(); int m = cin.nextInt(); if(n == 0 && m == 0) { return; } PLG(n,m); } } static void PLG(int n,int m) { if(n%2 == 0 || m % 2 == 0) { System.out.println("Wonderful!"); } else { System.out.println("What a pity!"); } } }
package Combat.com; import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String []args) { Scanner cin = new Scanner(System.in); while(cin.hasNext()) { int m = cin.nextInt(); int n = cin.nextInt(); PLG(m,n); } } static void PLG(int m,int n) { if(m % (n+1) == 0) { System.out.println("none"); } else { if(m <= n) { for(int i = m; i <= n; i++) { if(i!= m) { System.out.print(" "); } System.out.print(i); } System.out.println(); } else { int flag = 0; for(int i = 1; i <= n; i++) { if((m-i)%(n+1) == 0) { if(flag == 0) { System.out.print(i); } else { System.out.print(" " + i); } flag++; } } System.out.println(); } } } }
package Combat.com; import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String []args) { Scanner cin = new Scanner(System.in); int C = cin.nextInt(); while(C != 0) { int n = cin.nextInt(); int m = cin.nextInt(); PLG(n,m); C--; } } static void PLG(int n,int m) { if(n <= m) { System.out.println("Grass"); } else { if(n % (m+1) == 0) { System.out.println("Rabbit"); } else { System.out.println("Grass"); } } } }
package Combat.com; import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String []args) { Scanner cin = new Scanner(System.in); while(cin.hasNext()) { int n = cin.nextInt(); int p = cin.nextInt(); int q = cin.nextInt(); PLG(n,p,q); } } static void PLG(int n,int p,int q) { if(n < p+q) { if(n <= p) { System.out.println("LOST"); } else { System.out.println("WIN"); } } else if(n%(p+q) == 0) { System.out.println("WIN"); } else//有坑 { if(n % (p+q) > p) System.out.println("WIN"); else System.out.println("LOST"); } } }
威佐夫博弈:
一定要去百度百科上面,先理解透意思。
下面是一些威佐夫博弈的总结:
设当前局势为(a,b); a <= b
①a==b:同时从两堆取走a个石子,转化为(0,0)
②a==a[k]&&b>b[k]:从第二堆取走b−b[k]个石子,转化为(a,b[k])
③a==a[k]&&b<b[k]:同时从两堆取走a−a[b−a]个石子,转化为(a[b−a],b−a+a[b−a])
④a>a[k]&&b==b[k]:从第一堆取走a−a[k]个石子,转化为(a[k],b)
⑤a<a[k]&&b==b[k]:若a==a[j] (j<k),则从第二堆取走b−b[j]个石子,转化为(a,b[j]);
否则必有a==b[j](j<k)a==b[j](j<k),则从第二堆取走b−a[j]b−a[j]个石子,转化为(a[j],a)
例如5 8 ,5>a(8-5)=a3=4 8>b(8-5)=b3=7 从两堆中取走a-a(b-a)=5-4=1个,变成奇异局势(4,7)。
例如4 6 ,4>a(6-4)=a2=3 6>b(6-4)=b2=5 从两堆中取走a-a(b-a)=4-3=1个,变成奇异局势(3,5)。
可以理解成变成差为b-a的奇异局势。
如果a=bk并且b-a!=k,则从b堆中取走b-ak个,变成奇异局势(ak,bk).
例如,5 10 ,5=b2 10-5=5!=2 则从10中取走10-a2=10-3=7个,变成奇异局势(3,5)。
为什么要b-a!=k呢?例如7 10 , 7=a3,10-7=3=k 也可以变成奇异局势(4,7)。但这已经在4)判断过了。
package Combat.com; import java.util.Arrays; import java.util.Scanner; public class Main { static final double mid = (Math.sqrt(5)+1)/2; public static void main(String []args) { Scanner cin = new Scanner(System.in); while(cin.hasNext()) { int a = cin.nextInt(); int b = cin.nextInt(); int MAX = Math.max(a, b); int MIN = Math.min(a, b); int temp = (int) ((MAX-MIN)*mid); if(temp == MIN) { System.out.println("0"); } else { System.out.println("1"); } } } }
HDU2177的代码实现如下:巧妙暴力,分情况太麻烦了。
package Combat.com;
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
static final double MID = (Math.sqrt(5)+1)/2;
public static void main(String []args)
{
Scanner cin = new Scanner(System.in);
while(cin.hasNext())
{
int a = cin.nextInt();
int b = cin.nextInt();
if(a == 0 && b == 0)
{
return;
}
WTFGame(a,b);
}
}
static void WTFGame(int a,int b)
{
int temp = (int) ((b-a)*MID);
if(temp == a)
{
System.out.println("0");
}
else
{
System.out.println("1");
for(int i = 1; i <= a; i++)//先取同样石子
{
int n = a-i;
int m = b-i;
temp = (int) ((m-n)*MID);
if(temp == n)
{
System.out.println(n + " " + m);
}
}
for(int i = a-1; i >=0; i--)//从最小堆单取;
{
temp = (int) ((b-i)*MID);
if(temp > i)//因为a越小,temp就越大,永远不可能等。
{
break;
}
}
for(int i = b-1; i >= 0; i--)//从最大堆单取
{
int n = a;
int m = i;
if(n > m)
{
int t = a;
n = m;
m = t;
temp = (int)((m-n)*MID);
if(temp > n)//这里充当优先,当条件满足,无需进行下去了。
{
break;
}
}
temp = (int) ((m-n)*MID);
if(temp == n)
{
System.out.println(n + " " + m);
}
}
}
}
}
尼姆博弈(Nimm Game):
尼姆博弈指的是这样一个博弈游戏: 有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件, 取到最后一件物品的人获胜。
百度百科:
结论就是:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。
代码实现如下;
package Combat.com; import java.util.Arrays; import java.util.Scanner; public class Main { static final int MAX = 200005; static int array[] = new int[MAX]; public static void main(String []args) { Scanner cin = new Scanner(System.in); while(cin.hasNext()) { int m = cin.nextInt(); if(m == 0) { return; } int sum = 0;//异或的结果 for(int i = 0; i < m; i++) { array[i] = cin.nextInt(); sum = sum^array[i]; } NIMGame(sum,m); } } static void NIMGame(int sum,int k) { if(sum == 0)//代表面临奇异情况,必输 { System.out.println("No"); return; } else { System.out.println("Yes"); for(int i = 0; i < k; i++)//胜的第一次取法, { int s = sum^array[i];//结果s相当于,sum没与array[i]异或。 if(s < array[i]) { System.out.println(array[i] + " " + s); } } } } }
阶梯博弈:
具体意思,参照下面网址:https://www.cnblogs.com/jiangjing/p/3849284.html