Week 2-3 博弈

思维题

P7873 「SWTR-07」Scores(easy version) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目大意:构造一个n*m的矩阵,对于i行,保证不存在j行所有对应的列的元素大于i行。

思路:第一列按1-n递增,第二列按n-1-0递减,其他元素均为0,满足条件。

#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
    int t;
    cin>>t;
    cin>>t;
    while(t--){
        int m,n,a[105][105];
        int i,j;
        cin>>n>>m;
        if(n!=1 && m==1) {cout<<"NO"<<endl;continue;}
        cout<<"YES"<<endl;
        if(m==1) {cout<<"3"<<endl;continue;}
        for(i=0;i<n;i++)
        {
            a[i][0]=i+1;
            a[i][1]=n-i-1;
        }
        for(i=0;i<n;i++)
        {
            for(j=2;j<m;j++)
                a[i][j]=0;
        }
        for(i=0;i<n;i++)
        {

            for(j=0;j<m;j++)
                cout<<a[i][j]<<" ";
            cout<<endl;
        }
    }
    return 0;
}

P7874 「SWTR-07」My rating is -32(easy version) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

剑指 Offer 62. 圆圈中最后剩下的数字 - 力扣(LeetCode) (leetcode-cn.com)

用队列写容易超时:

class Solution {
public:
    int lastRemaining(int n, int m) {
         queue<int>a;
        int i,j;
        for(i=0;i<=n-1;i++)
          a.push(i);
          int count=1;
         while(a.size()>1)
         {
               int t;
                while(count%m!=0)
                {
                    t=a.front();
                    a.push(t);
                    a.pop(); 
                   count++;
                }
                a.pop();
                count=1;
          }     
          return a.front();
    }
};

参考评论区(dp:来源:胎教毕业也能懂,动态规划求解约瑟夫环问题 - 圆圈中最后剩下的数字 - 力扣(LeetCode) (leetcode-cn.com)https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/tai-jiao-bi-ye-ye-neng-dong-dong-tai-gui-zmwj/

class Solution {
  public int lastRemaining(int n, int m) {
    int ans = 0;
    for(int i = 2;i <= n;i++) ans = (ans+m)%i;
    return ans;
  }
}

Problem - B - Codeforces

题目大意:对于一个字符串 每个字符重复s[i]-'a'+1遍。查询一个范围,求这个范围的字符个数

前缀和结局问题;

#include <iostream>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
   int n,q,l,r;
   string s;
   cin>>n>>q;
   cin>>s;
   int a[100000+20]={0},i,j;
   a[0]=s[0]-'a'+1;
   for(i=1;i<n;i++)
   {
       a[i]=a[i-1]+s[i]-'a'+1;
   }
   while(q--)
   {
       cin>>l>>r;
       if(l-1<=0) cout<<a[r-1]<<endl;
       else cout<<a[r-1]-a[l-2]<<endl;
   }
}

补题目 Problem - C - Codeforces

题目大意:一群人站一排,每个人都有对应数字,并且有初始方向 right。若交换两个元素,那么他们的方向会随之交换并取反。求多少次操作是他们不递减,并且方向均朝right。

思路:为了是方向不变,则最后交换,偶数依然在偶数位置,奇数在奇数位置。

#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
long long  int num[5][100000+20];
int main()
{
   int t,n,q,l,r;
   cin>>t;
   while(t--)
   {
       long long int a[100000+20],i,j;

       cin>>n;
       memset(num,0,sizeof(num));
       for(i=1;i<=n;i++)
       {
           cin>>a[i];
           if(i%2==0) num[0][a[i]]++;
           else num[1][a[i]]++;
       }
       sort(a+1,a+1+n);
       int flag=1;
       for(i=1;i<=n;i++)
       {
           if(num[i%2][a[i]]==0)
           {
               flag=0;
               break;
           }
           num[i%2][a[i]]--;
       }
     if(flag==0) printf("NO\n");
     else cout<<"YES"<<endl;
   }
}




专题内容:

一,巴什博弈。

只有一堆n个物品,两个人轮流从这堆物品中取物, 规定每次至少取一个,最多取m个。最后取光者得胜。巴什博弈 · ACM Book (gitbooks.io)

例题:292. Nim 游戏 - 力扣(LeetCode) (leetcode-cn.com)

class Solution {
public:
    bool canWinNim(int n) {
         if(n%4!=0) return true;
         else return false;
    }
};

1025. 除数博弈 - 力扣(LeetCode) (leetcode-cn.com)
找规律:当n为奇数是,对方赢,否则自己赢。

class Solution {
public:
    bool divisorGame(int n) {
         if(n%2==0) return true;
         else return false;
    }
};

 来自官方:除数博弈 - 除数博弈 - 力扣(LeetCode) (leetcode-cn.com)

题目大意:

思路:

Problem - 4764 (hdu.edu.cn)

题目大意:

思路:必胜态为(N-1)%(k+1)==0;如果(n-1)%(k+1)==0,不论tang怎么取数字,jiang怎么凑成k+1,jiang必胜。如果(n-1)%(k+1)!=0,tang先取走多的部分。

#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
    int n,k;
    while(cin>>n>>k && n!=0 && k!=0)
    {
        
        if((n-1)%(k+1)==0) cout<<"Jiang"<<endl;
        else cout<<"Tang"<<endl;
    }
    return 0;
}

二:威佐夫博奕(Wythoff Game)

威佐夫博弈(Wythoff's game):有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

1.从任意一堆中取任意个 > 1。
2.从两堆中取同样多个。

结论:
对于任意的局势(a, b)(a < b),必败点为(b-a) * (sqrt(5)+1)/2 = a.

来源(威佐夫博弈_百度百科 (baidu.com));

思维题:Problem - A - Codeforces

题目大意:数组元素有1,2,3,,,,,2n构成,问有多少个数组满足以下条件:pi<pi+1 的个数大于n。

解题思路:解题全靠猜 :(2n)!/2

代码:

#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--)
    {
	int n; cin>>n;
	long long int sum = 1; for(int i = 3; i<=2*n; i++) sum = (sum * i) % 1000000007;
	cout<<sum<<endl;
    }
}
 

专题:P2252 [SHOI2002]取石子游戏|【模板】威佐夫博弈 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

模板题

有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

1.从任意一堆中取任意个 大于 1。
2.从两堆中取同样多个。

结论:
对于任意的局势(a, b)(a < b),必败点为(b-a) * (sqrt(5)+1)/2 = a.

#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
#include<math.h>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int a,b;
	cin>>a>>b;
	if(a>b) swap(a,b);
	if((int)(double(b-a)*(sqrt(5)+1)/2)==a) cout<<"0"<<endl;
	else cout<<"1"<<endl;
}

取(2堆)石子游戏 - HDU 2177 - Virtual Judge (vjudge.net)

题目大意:与上一题大致相似,不过要求输出第一个人取的方案。

思路:

#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
#include<math.h>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	long long int a,b;
	while(cin>>a>>b && (a || b))
       {
          if(a==int((double(b-a) )* (sqrt(5)+1)/2))
            {
                cout<<"0"<<endl;
                continue;
            }
            else{
                cout<<"1"<<endl;
                int i,j;
                   int n,m;
                for(i=1;i<=a;i++)
                {

                    n=a-i;
                    m=b-i;
                    if(n==int((double(m-n) )* (sqrt(5)+1)/2))//此时n<m
                    {
                        cout<<n<<" "<<m<<endl;
                        break;
                    }
                }
                for(i=0;i<=b;i++)
                {
                    int n,m;
                    n=i;
                    m=a;
                    if(m<n) swap(m,n);
                    if(n==(int((double(m-n) )* (sqrt(5)+1)/2)))
                    cout<<n<<" "<<m<<endl;
                }

            }
       }
    return 0;
}

思维题:Problem - B - Codeforces(补题)

 题目大意:有一个n个点,m条边的连通无向图,并且任意两点的最小距离(其中最小距离的最大距离成为直径)小于k-1,求是否存在。

思路:1.当m<n-1是,不连通。2.当m>(n-1)n/2时,超出完全图。3.n==1是m=0,且k>=2.

4.m==(n-1)n/2,此时为完全图,距离为1,所以k>=3。5.n-1<m<(n-1)n/2,可构成菊花图,此时直径为2,k>=4.

注意数据范围;

#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
#include<math.h>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	long long int t,n,m,k;
	cin>>t;
	while(t--)
    {
        cin>>n>>m>>k;
        if(m<n-1|| m>((n-1)*n/2) || k<=1) cout<<"NO"<<endl;
        else if(n==1){
                if(m==0 && k>=2) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;

        }
        else if(m==(n-1)*n/2)
        {
            if(k>=3) cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
        else
        {
            if(k>=4) cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    }
    return 0;
}

三.NIM游戏

Nim游戏_百度百科 (baidu.com)

P2197 【模板】nim 游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目大意:地上有 n 堆石子(每堆石子数量小于 10^4),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 n 堆石子的数量,他想知道是否存在先手必胜的策略

思路:对于一个局面,当且仅当A1 xor A2 xor ... xor AN =0时,该局面为P局面。

代码:

#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
#include<math.h>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t,n,i,a[10000+10];
	cin>>t;
	while(t--)
    {
        cin>>n;
        int k,sum=0;
        for(i=0;i<n;i++)
        {
            cin>>k;
            sum^=k;
        }
        if(sum==0) cout<<"No"<<endl;
        else cout<<"Yes"<<endl;
    }
    return 0;
}

SG函数(一般先打表)

Fibonacci again and again - HDU 1848 - Virtual Judge (vjudge.net)t

题意:1、  这是一个二人游戏;
2、  一共有3堆石子,数量分别是m, n, p个;
3、  两人轮流走;
4、  每走一步可以选择任意一堆石子,然后取走f个;
5、  f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);
6、  最先取光所有石子的人为胜者;

思路:运用sg函数 后手必胜当且仅当sg的异或和为0。此题目参考打表搜索 [学习笔记] (博弈论)Nim游戏和SG函数_A_Comme_Amour的博客-CSDN博客

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
	int f[25]={1,2,3},sg[1005],s[1005],i,j;
	int n,m,p;
	sg[0]=0;
//sg打表
        for(i=3;i<15;i++) f[i]=f[i-1]+f[i-2];//f表示可以取走的石子的数量
        for(i=0;i<=1003;i++)
        {
            memset(s,0,sizeof(s));
            for(j=0;j<15 && f[j]<=i;j++) s[sg[i-f[j]]]=1;//循环让每一种取法的考虑到

            for(j=0;j<=1003;j++) if(!s[j]) {sg[i]=j;break;}
        }

        while(scanf("%d%d%d",&n,&m,&p) )
    {
        if(!n && !m && !p) break;
        if(sg[n]^sg[m]^sg[p]) puts("Fibo");
        else  puts("Nacci");
    }
}

Problem - 1536 (hdu.edu.cn)

题目大意:首先输入k和k种一次能取的个数,在输入m表示询问次数,每次询问需要输入不同堆的石头的个数。

思路:运用sg函数模板,最后将结果抑或,若为0则对面赢。

注意:输出时必须同时输出。

代码:

#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int sg[10008];
bool vis[10008];
int f[100+20];
int k;
void getSG(int n)
{
    sort(f+1,f+1+k);
    memset(sg,0,sizeof(sg));
    for (int i=1; i<=n; i++)
    {
        memset(vis,0,sizeof(vis));
        for (int j=1; f[j]<=i && j<=k; j++)//f排序是为了让每一种取法都循环到
            vis[sg[i-f[j]]]=1;
        for (int j=0; j<=n; j++)
        {
            if (vis[j]==0)
            {
                sg[i]=j; break;
            }
        }
    }
}
int main()
{
   int i,j,m;
   while(cin>>k && k)
   {
       for(i=1;i<=k;i++)
        cin>>f[i];
       cin>>m;
       int a[100+20];
       getSG(10008);
        string ans="";
       while(m--)
       {
           int l;
           cin>>l;
        int sum=0;
           for(i=1;i<=l;i++)
            {cin>>a[i];
            sum=sum^sg[a[i]];
            }
            if(sum==0)   ans+="L";
            else ans+="W";
       }
       cout<<ans<<endl;
   }
   return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值