欢迎使用信阳师范学院在线评测(Online Judge)平台!
2091: 取石子(七)
时间限制: 1 Sec 内存限制: 64 MB
提交: 10 解决: 7
您该题的状态:已完成
[提交][状态][讨论版]
题目描述
Yougth和Hrdv玩一个游戏,拿出n个石子摆成一圈,Yougth和Hrdv分别从其中取石子,谁先取完者胜,每次可以从中取一个或者相邻两个,Hrdv先取,输出胜利着的名字。
输入
输入包括多组测试数据。 每组测试数据一个n,数据保证int范围内。
输出
输出胜利者的名字。
样例输入
样例输出
这道题,猛一看,没思路,后来,上网搜了一下有什么规律,搜到了这个
下面这段来自白白の屋的文章的一段:
巴什博弈:只有一堆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)的倍数,就能最后获胜。那么这个时候只要n%(m+1)!=0,先取者一定获胜。
这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。
分析此类问题主要放法是:P/N分析:
P点:即必败点,某玩家位于此点,只要对方无失误,则必败;
N点:即必胜点,某玩家位于此点,只要自己无失误,则必胜。
三个定理:
一、所有终结点都是必败点P(上游戏中,轮到谁拿牌,还剩0张牌的时候,此人就输了,因为无牌可取);
二、所有一步能走到必败点P的就是N点;
三、通过一步操作只能到N点的就是P点;
巴什博弈的一个最重要的特征就是只有一堆。然后就在其中改,要么在范围内不规定个数,要么就规定只能取几个,再要么就倒过来,毕竟是最简单的博弈,代码相对而言较短额~
例题1:NYOJ 23(取石子游戏一),巴什博弈,只要n%(m+1)!=0,则先取者一定获胜。
[cpp] view plaincopy
- #include<iostream>
- using namespace std;
- int main()
- { int n,m,Case;
- cin>>Case;
- while(Case--)
- { cin>>n>>m;
- cout<<(n%(m+1)?"Win":"Lose")<<endl;
- }
- return 0;
- }
例题2:HDU 2149,英语标题额,原来是中文题。反过来就是一样的啦,只是取数时的规律是:如果M%(N+1)!=0,那么第一个取的数就是M%(N+1),留给对手的是(N+1)的倍数,还有就是M<N的情况,不说你也懂,开始写了sort的,但后来一看可以不要,代码:
[cpp] view plaincopy
- #include<iostream>
- using namespace std;
- int main()
- { int n,m;
- while(scanf("%d%d",&m,&n)!=EOF)
- { int sum=0;
- if(m%(n+1)==0) cout<<"none";
- else
- { if(m%(n+1)) cout<<m%(n+1); //直接取余数
- if(n>=m)
- { for(int i=m+1;i<=n;i++)
- cout<<" "<<i;
- }
- }
- cout<<endl;
- }
- return 0;
- }
题3:HDU 1847,寻找必败态,n%3=0则Cici赢,否则Kiki赢。
分析:(1)、若是留给Cici的是3,那么Cici只能取1个或2个,所以再轮到Kiki取的话必赢。
(2)、若是给Cici留下的是3的倍数,假设为3n(n=1,2,3,..),那么无论Cici取什么数,剩余的数一定可以写成3m+1或者3m+2(m<n)的形式,那么只要Kiki再取的时候留给Cici的仍然是3的倍数的话,就必胜了。代码略。
当然这种题目可以直接先枚举前面几个数,就能找到规律啦~
心里想着这么简单,提交上去,遂卒。
注意红色字体,
后来,又搜到一篇,原来此题非巴什博弈
摆成一圈,当总的石子数大于2时,
如:n=5,a(先取者)拿一个,b再拿一个,使剩下的分成两段,还剩3个,无论a拿几个,b就等笑吧,哈哈哈。
当a拿两个,b拿一个,同样使剩下的分成两段,嘿嘿,然后a就唧唧了,哈哈哈(原谅我忍不住内心的喜悦)
n=6,a(先拿着)拿一个,b拿两个,同样使剩下的分成两段,然后a就完了
a拿两个,b拿一个,同样使剩下的分成两段,然后a卒。
(其他的n大于2的情况类推)
n小于2,就不说了。
代码太简单了。
不管怎么说,挺有意思的。
欢迎使用信阳师范学院在线评测(Online Judge)平台!
1831: 阶乘因式分解(一)
时间限制: 3 Sec 内存限制: 64 MB
提交: 41 解决: 35
您该题的状态:已完成
[提交][状态][讨论版]
题目描述
给定两个数m,n,其中m是一个素数。
将n(0<=n<=10000)的阶乘分解质因数,求其中有多少个m。
输入
第一行是一个整数s(0<s<=100),表示测试数据的组数
随后的s行, 每行有两个整数n,m。
输出
输出m的个数。
样例输入
<span style="color:#333333"><span style="color:black">2
100 5
16 2
</span></span>
样例输出
<span style="color:#333333"><span style="color:black">24
15</span></span>
质因数:就是因数里面是质数的数。
首先,怎么求因数?
#include<stdio.h>
int main()
{
int n;
while(~scanf("%d",&n))
{
int i=2;
while(n>1)
{
if(n%i==0)
{
printf("%d ",i);
n/=i;
}
else
i++;
}
printf("\n");
}
return 0;
}
这是最简单的分解因数的方法。(思想:短除法)
但是他说的是阶乘里的某个质因数的出现次数?????????
#include<stdio.h>
int
main()
{
int
n,i,a,b;
scanf
(
"%d"
,&n);
while
(n--)
{
int
j=0,m;
scanf
(
"%d %d"
,&a,&b);
while
(a!=0)
{
m=a;
while
(m%b==0)
{
//printf("%d\n",m);
//if(i==b)
j++;
m/=b;
}
a--;
}
printf
(
"%d\n"
,j);
}
return
0;
}
思想就是,从n开始,让它除这个质数,除到不能除为止,用计数器计数,然后n--。
但是如果他不指定质数是谁?那怎么求?
for
(i = 2; i * i <= n; i ++) {
if
(n % i == 0) {
while
(n % i == 0) 这个循环可厉害啦,经过他 n的因数中i的倍数就没了
{
count ++;
n /= i;
}
}
}
1832: 6174问题
时间限制: 1 Sec 内存限制: 64 MB
提交: 51 解决: 26
[提交][状态][讨论版]
题目描述
假设你有一个各位数字互不相同的四位数,把所有的数字从大到小排序后得到a,从小到大后得到b,然后用a-b替换原来这个数,并且继续操作。例如,从1234出发,依次可以得到4321-1234=3087、8730-378=8352、8532-2358=6174,又回到了它自己!现在要你写一个程序来判断一个四位数经过多少次这样的操作能出现循环,并且求出操作的次数
比如输入1234执行顺序是1234->3087->8352->6174->6174,输出是4
输入
第一行输入n,代表有n组测试数据。 接下来n行每行都写一个各位数字互不相同的四位数
输出
经过多少次上面描述的操作才能出现循环
样例输入
1 1234
样例输出
4
第一次看着道题,没思路,感觉能写,但是要先理清思路,
#include<iostream>
#include<algorithm> 用到了sort函数,他的时间复杂度是n倍的logn,相当于快排
using
namespace
std;
bool
cmp(
int
a,
int
b)
{
return
a>b; 这个cmp是sort的第三个参数,如果没有的话,默认从小到大。否则,cmp函数,return a>b 从大到小
反之,从小到大。
}
int
main()
{
int
n,m;
scanf
(
"%d"
,&n);
while
(n--)
{
scanf
(
"%d"
,&m);
int
a[4];
int
i=0,j,k=1,k1=0,k2=0;
j=m;
while
(j!=0)
{
a[i]=j%10;
j/=10;
i++;
}
//for(i=0;i<4;i++)
//printf("%d",a[i]);
//printf("\n");
while
(1)
{
while
(j!=0)
{
a[i]=j%10;
j/=10;
i++;
}
sort(a,a+4);
//从小到大
for
(i=0;i<4;i++)
k2=k2*10+a[i];
sort(a,a+4,cmp);
//从大到小
for
(i=0;i<4;i++)
k1=k1*10+a[i];
//printf("k1:%d k2;%d\n",k1,k2);
k++;
j=k1-k2;
//printf("%d\n",j);
if
(j==6174)
break
;
k1=0;k2=0;i=0;
}
cout<<k<<endl;
}
return
0;
}
思路:将输入的数保存到数组(整形)里,然后用sort函数从小到大排,从大到小排,分别转变成一个整数,比较他们的差,是否等于6174,。
在网上找到一篇代更简洁的:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
int n,m,p,a[4],b,b1,b2;
cin>>n;
while(n--){
cin>>m;
p=1;
while(m!=6174){
for(i=0;i<4;i++){
a[i]=m%10;
m=m/10;
}
sort(a,a+4);
b2=a[3]*1000+a[2]*100+a[1]*10+a[0];
b1=a[0]*1000+a[1]*100+a[2]*10+a[3];
b=b2-b1;
p++;
m=b;
}
cout<<p<<endl;
}
return 0;
}
1837: 笨小熊
时间限制: 2 Sec 内存限制: 64 MB
提交: 50 解决: 22
您该题的状态:已完成
[提交][状态][讨论版]
题目描述
笨小熊的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!
这种方法的具体描述如下:假设maxn是单词中出现次数最多的字母的出现次数,minn是单词中出现次数最少的字母的出现次数,如果maxn-minn是一个质数,那么笨小熊就认为这是个Lucky Word,这样的单词很可能就是正确的答案。
输入
第一行数据N(0<N<100)表示测试数据组数。
每组测试数据输入只有一行,是一个单词,其中只可能出现小写字母,并且长度小于100。
输出
每组测试数据输出共两行,第一行是一个字符串,假设输入的的单词是Lucky Word,那么输出“Lucky Word”,否则输出“No Answer”; 第二行是一个整数,如果输入单词是Lucky Word,输出maxn-minn的值,否则输出0
样例输入
<span style="color:#333333"><span style="color:black">2
error
olympic</span></span>
样例输出
<span style="color:#333333"><span style="color:black">Lucky Word
2
No Answer
0</span></span>
#include<stdio.h>
#include<string.h>
int
main()
{
int
n,i,j;
char
a[100];
scanf
(
"%d"
,&n);
while
(n--)
{
scanf
(
"%s"
,a);
int
b[
strlen
(a)],max=0,min=1000,flag=0;
for
(i=0;i<
strlen
(a);i++)
b[i]=0;
for
(i=0;i<
strlen
(a);i++)
{
for
(j=0;j<
strlen
(a);j++)
{
if
(a[i]==a[j])
b[i]+=1;
}
}
/*for(i=0;i<strlen(a);i++)
{
printf("b[%d]:%d ",i,b[i]);
}
printf("\n"); */
for
(i=0;i<
strlen
(a);i++)
{
if
(max<b[i])
max=b[i];
if
(min>b[i])
min=b[i];
}
//printf("%d %d\n",max,min);
if
(max-min==2)
flag=1;
else
for
(i=2;i<max-min;i++)
{
if
((max-min)%i==0)
{
flag=0;
break
;
}
}
if
(i>=max-min)
flag=1;
if
(flag==1&&max-min!=0&&max-min!=1)
printf
(
"Lucky Word\n%d\n"
,max-min);
else
printf
(
"No Answer\n0\n"
);
}
return
0;
}
上图左边为正确的,右为错的。
当测试数据为addddd ,max-min为4,4不是质数,但右边输出了lucky word,因为他从3开始,就跳出去了。
1893: 分数加减法
时间限制: 3 Sec 内存限制: 64 MB
提交: 14 解决: 8
您该题的状态:已完成
[提交][状态][讨论版]
题目描述
编写一个C程序,实现两个分数的加减法
输入
输入包含多行数据 每行数据是一个字符串,格式是"a/boc/d"。 其中a, b, c, d是一个0-9的整数。o是运算符"+"或者"-"。 数据以EOF结束 输入数据保证合法
输出
对于输入数据的每一行输出两个分数的运算结果。 注意结果应符合书写习惯,没有多余的符号、分子、分母,并且化简至最简分数
样例输入
<span style="color:#333333"><span style="color:black">1/8+3/8
1/4-1/2
1/3-1/3</span></span>
样例输出
<span style="color:#333333"><span style="color:black">1/2
-1/4
0</span></span>
这道题我第一次做时,还用字符串读取,我天,好麻烦啊!
后来我突然想到用格式化读取,见代码就so easy了。
#include<stdio.h>
int
main()
{
int
a,b,c,d;
char
k;
while
(~
scanf
(
"%d/%d%c%d/%d"
,&a,&b,&k,&c,&d))
{
if
(a==0&&b==0)
{
if
(c==0&&d==0)
{
printf
(
"0\n"
);
continue
;
}
if
(k==
'-'
)
{
printf
(
"%c%d/%d\n"
,k,c,d);
continue
;
}
else
printf
(
"%d/%d\n"
,c,d);
continue
;
}
if
(c==0&&d==0)
{
printf
(
"%d/%d\n"
,a,b);
continue
;
}
a*=d;
c*=b;
b*=d;
if
(k==
'+'
)
a+=c;
else
a-=c;
if
(a<=b)
for
(
int
i=2;i<b;)
{
if
(a%i==0&&b%i==0)
while
(a%i==0&&b%i==0)
{
a/=i;
b/=i;
}
else
i++;
}
else
for
(
int
i=2;i<a;)
{
if
(a%i==0&&b%i==0)
while
(a%i==0&&b%i==0)
{
a/=i;
b/=i;
}
else
i++;
}
if
(a==0||b==0)
printf
(
"0\n"
);
else
{
if
(a%b==0)
printf
(
"%d\n"
,a/b);
else
printf
(
"%d/%d\n"
,a,b);
}
}
return
0;
}
在沾一个简洁的:
#include<stdio.h>
#include<math.h>
int
main()
{
int
a,b,c,d,x,y,p,i;
char
o;
while
(~
scanf
(
"%d/%d%c%d/%d"
,&a,&b,&o,&c,&d))
{
y=b*d;
if
(o==
'+'
) x=a*d+b*c;
else
x=a*d-b*c;
if
(x>y) p=x;
else
p=y;
for
(i=2;i<=
sqrt
(p);i++)
{
if
(x%i==0&&y%i==0)
{
x/=i;
y/=i;
i=2;
}
}
if
(x==0||y==0)
printf
(
"0\n"
);
else
if
(y==1)
printf
(
"%d\n"
,x);
else
printf
(
"%d/%d\n"
,x,y);
}
return
0;
}
但有漏洞,数据:9/8-1/8 它输出2/2,还差一个约分。