【NOI2011】兔兔与蛋蛋的游戏【二分图博弈】

本文深入探讨了一种基于棋盘的游戏算法,通过反证法证明了棋子移动路径的无交特性,进而优化了游戏策略。文章利用C++实现了一个算法模型,通过对棋盘状态的分析,实现了对游戏进程的有效预测。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

结论 不会有同一个棋子移动两次

反证法,对于第一个移动第二次的棋子

设两次移动之间(含)的移动的棋子为 A 1 , A 2 , A 3 , … … , A n A_1,A_2,A_3,……,A_n A1,A2,A3,An(指棋子本身而非位置)

因为最后移回来了,所以往上和往下、往左和往右次数相同

所以 n n n是偶数 然后 A 1 A_1 A1 A n A_n An颜色不同

但这个棋子开始被移到当前位置,最后被移走,所以 A 1 = A n A_1=A_n A1=An,矛盾

所以移动路径没有交

既然这样,我们就不用考虑棋盘具体的变化,而看成空格交替走两个颜色

然后和上一道题就一样了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 2005
#define MAXM 10005
using namespace std;
struct edge{int u,v;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
void addnode(int u,int v)
{
     e[++cnt]=(edge){u,v};
     nxt[cnt]=head[u];
     head[u]=cnt;
}
int n,m;
#define id(x,y) (((x)-1)*m+(y))
char s[50][50];
const int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
bool del[MAXN];
int link[MAXN],used[MAXN];
bool find(int u)
{
     for (int i=head[u];i;i=nxt[i])
	  if (!used[e[i].v]&&!del[e[i].v])
	  {
	       used[e[i].v]=1;
	       if (!del[link[e[i].v]]&&(!link[e[i].v]||find(link[e[i].v])))
	       {
		    link[u]=e[i].v,link[e[i].v]=u;
		    return true;
	       }
	  }
     return false;
}
int calc()
{
     memset(link,0,sizeof(link));
     int ans=0;
     for (int x=1;x<=n;x++)
	  for (int y=1;y<=m;y++)
	       if (s[x][y]!='O'&&!del[id(x,y)])
		    memset(used,0,sizeof(used)),ans+=find(id(x,y));
     return ans;
}
bool win[MAXN];
int main()
{
     scanf("%d%d",&n,&m);
     for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
     int st;
     for (int x=1;x<=n;x++)
	  for (int y=1;y<=m;y++)
	       if (s[x][y]!='O')
	       {
		    for (int i=0;i<4;i++)
			 if (s[x+dx[i]][y+dy[i]]=='O')
			      addnode(id(x,y),id(x+dx[i],y+dy[i]));
		    if (s[x][y]=='.') st=id(x,y);
	       }
     int k,tot=0;
     scanf("%d",&k);
     int mmat=calc();
     for (int i=1;i<=2*k;i++)
     {
	  del[st]=1;
	  int t=calc();
	  if (mmat!=t) win[i]=1;
	  mmat=t;
	  int x,y;
	  scanf("%d%d",&x,&y);
	  st=id(x,y);
     }
     for (int i=1;i<=k;i++) if (win[2*i-1]&&win[2*i]) ++tot;
     printf("%d\n",tot);
     for (int i=1;i<=k;i++) if (win[2*i-1]&&win[2*i]) printf("%d\n",i); 
     return 0;
}

资源下载链接为: https://pan.quark.cn/s/f989b9092fc5 今天给大家分享一个关于C#自定义字符串替换方法的实例,希望能对大家有所帮助。具体介绍如下: 之前我遇到了一个算法题,题目要求将一个字符串中的某些片段替换为指定的新字符串片段。例如,对于源字符串“abcdeabcdfbcdefg”,需要将其中的“cde”替换为“12345”,最终得到的结果字符串是“ab12345abcdfb12345fg”,即从“abcdeabcdfbcdefg”变为“ab12345abcdfb12345fg”。 经过分析,我发现不能直接使用C#自带的string.Replace方法来实现这个功能。于是,我决定自定义一个方法来完成这个任务。这个方法的参数包括:原始字符串originalString、需要被替换的字符串片段strToBeReplaced以及用于替换的新字符串片段newString。 在实现过程中,我首先遍历原始字符串,查找需要被替换的字符串片段strToBeReplaced出现的位置。找到后,就将其替换为新字符串片段newString。需要注意的是,在替换过程中,要确保替换操作不会影响后续的查找和替换,避免遗漏或重复替换的情况发生。 以下是实现代码的大概逻辑: 初始化一个空的字符串result,用于存储最终替换后的结果。 使用IndexOf方法在原始字符串中查找strToBeReplaced的位置。 如果找到了,就将originalString中从开头到strToBeReplaced出现位置之前的部分,以及newString拼接到result中,然后将originalString的查找范围更新为strToBeReplaced之后的部分。 如果没有找到,就直接将剩余的originalString拼接到result中。 重复上述步骤,直到originalStr
题目描述 给出一个由 $n$ 个数排成的序列,你可以进行如下操作: 交换相邻的两个数。 将该序列中的第 $k$ 个数改为 $x$。 你的任务是将该序列变成一个等差数列。 输入格式 第一行包含两个整数 $n$ 和 $m$,表示序列长度和操作次数。 第二行包含 $n$ 个整数,表示初始序列。 接下来 $m$ 行,每行描述一个操作,格式如下: 1. swap x y:表示交换原序列中下标为 $x$ 和 $y$ 的数。 2. set x y:表示将原序列中下标为 $x$ 的数修改为 $y$。 输出格式 如果无法将序列变成等差数列,输出 Impossible。 否则,第一行输出 Yes。 第二行输出变换后的序列。 数据范围 $1 \leq n \leq 5000$ $1 \leq m \leq 10^5$ $1 \leq |a_i|, |x|, |y| \leq 10^9$ 输入样例1: 5 3 5 4 3 2 1 set 2 6 swap 1 2 set 4 1 输出样例1: Yes 6 4 3 1 2 输入样例2: 5 3 5 4 3 2 1 set 2 6 swap 1 2 set 4 10 输出样例2: Impossible 算法1 (二分图匹配) $O(n^3)$ 由于每次可以交换相邻的两个数,可以通过这个操作来使序列中的某个数字移动到另一个位置,因此可以考虑每个数字在序列中的位置之间建立一条边,如果两个数字之间可以通过交换相邻数变成等差数列,则在它们之间连一条边。 具体而言,如果有两个数字 $a,b$,假设它们当前的位置分别为 $i,j$,那么它们之间就可以连一条边,如果它们能够使得序列变成等差数列。 对于 $set$ 操作,可以考虑将该位置对应的点其他点之间的边全部删除,然后重新建立连接。 最后,判断是否存在一个匹配,使得所有点都另一个点进行匹配,如果存在,则说明可以通过交换相邻数的方式使得序列变成等差数列。 时间复杂度 - 建图 $O(n^2)$ - 匈牙利算法 $O(n^3)$ C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值