思维题
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();
}
};
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;
}
}
题目大意:对于一个字符串 每个字符重复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;
}
}
题目大意:一群人站一排,每个人都有对应数字,并且有初始方向 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)
题目大意:
思路:
题目大意:
思路:必胜态为(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.
题目大意:数组元素有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游戏
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");
}
}
题目大意:首先输入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;
}