这一题好狗血,用递推空间炸了,0分,(哭死在厕所 )。
题目:
wzms今年举办了一场剪刀石头布大赛,bleavesbleaves被选为负责人。 比赛共有2^n 个人参加,分为2n轮, 在每轮中,第 1位选手和第 2位选手对战,胜者作为新的第 1位选手, 第 3位和第 4位对战,胜者作为新的第 2位选手,以此类推。 调查得知,每个人都有其偏爱决策,每个人在每一次对战中都会使用他的偏爱决策。 如果一次对战的双方的偏爱决策相同,那么这次对战就永远不会结束,所以 不希望这种情况发生。 现在 知道了每个人的偏爱决策,但她不知道如何安排初始的次序,使得上面的情况不会发生,你能帮帮她吗?
输入格式:
一行三个整数R,P,SR,P,S ,表示偏爱石头,布,剪刀的人数分别为R,P,SR,P,S 。
输出格式:
如果无解,输出 IMPOSSIBLE ; 否则输出一个长度为R+P+S的字符串,第 ii个字符表示初始时第 ii位选手的偏爱决策, 如果有多种方案,输出字典序最小的。
输入输出样例:
输入样例1:
1 1 0
输出样例1:
PR
输入样例2:
2 0 0
输出样例2:
IMPOSSIBLE
输入样例3:
1 1 2
输出样例3:
PSRS
说明:
样例解释1
只有 22个选手,一个偏爱石头,一个偏爱布,无论次序如何,偏爱布的选手都会胜出。 所以方案可以是 PRPR和 RPRP,其中字典序最小的 。
题解:
这一题还好不会太难,根据题目要求我们求最终的序列,可是我们发现极其不好求,那么我们换一种思路,我们枚举每种最后胜利者的偏爱(只有三种),根据规则由最后胜利者我们推到参赛者的偏爱,这样所有的问题就都在字典序上了,我们可以这样,我们dfs从1,不断往下dfs每次范围乘以二(和归并排序类似,我们不是排序而是分解),然后我们在回溯时左右两个区间我们可以根据字典序判断谁在左边(即前面),这样我们最后只要找到了就好了,如果没找到则输出IMPOSSIBLE 就好了,时间复杂度O(n log n)。
上代码:
#include<iostream>
#include<cstdio>
using namespace std;
int r,s,p,ans[1100000],n,R,S,P;//0是布即p,1是石头即r,2是剪刀即s
void dfs(int x,int l,int r)//x表示我们的胜利者的偏爱,l和r表示参赛者的范围,就是总共2^n个人根据排序标号我们现在处理到[l,r]这个范围的人了
{
bool bj=false;//定义标记,在回溯时判断是否要调换
int mid=(l+r)>>1;
if(l==r)//到了边界
{
ans[l]=x;
if(x==0)
P++;
if(x==1)
R++;
if(x==2)
S++;
return ;
}
if(x==0)//偏爱是布
{
dfs(0,l,mid);
dfs(1,mid+1,r);
}
if(x==1)//偏爱是石头
{
dfs(1,l,mid);
dfs(2,mid+1,r);
}
if(x==2)//偏爱是剪刀
{
dfs(0,l,mid);
dfs(2,mid+1,r);
}
for(int i=l;i<=mid;i++)//暴力判断
{
if(ans[i]<ans[mid+i-l+1])
break;
if(ans[i]>ans[mid+i-l+1])
{
bj=true;
break;
}
}
if(bj)//暴力交换
for(int i=l;i<=mid;i++)
swap(ans[i],ans[mid+i-l+1]);
}
int main()
{
int x;
scanf("%d %d %d",&r,&p,&s);
x=r+s+p;//计算一下总共x个人
for(int i=0;i<=2;i++)//枚举胜利者的偏爱
{
R=0;//清空一下
S=0;//这三个是我们用来统计当前情况下三种偏爱的人的个数
P=0;
dfs(i,1,x);
if(R==r && S==s && P==p)//如果与题目相符合
{
for(int i=1;i<=x;i++)//输出
{
if(ans[i]==0)
printf("P");
if(ans[i]==1)
printf("R");
if(ans[i]==2)
printf("S");
}
cout <<endl;
return 0;//记得直接return掉,要不然下面的IMPOSSIBLE要用标记
}
}
printf("IMPOSSIBLE");//没有输出的话
return 0;
}
明天就要回去了,(不想军训 )。