题目链接
昨天晚上的cf,和程磊开黑打的,听说div3好上分,本来还指望这场上130分直接把颜色改了,没想到翻车了......就过了俩题,A和D过了,B题是个贪心,没写好。
A水题。
B:
题意:给你一个n的全排列,你可以操作n-1次(或者少于n-1次),问操作完后使字典序最小,输出这个字典序最小的排列。对于每种操作,你可以选一个数i(1
思路:每次把要操作的区间的最小值找出来,然后把他弄到区间的最前面,然后区间往右推移,直到结束。
这种题,我总是思路不太清晰,边码边写,弄得很乱,调不出来。还是应该先把思路弄清再写!
#include
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<a[i])
{
minn=a[i];
q=i;
}
}
if(minn==ans[p])
{p++;return ;}
for(int i=q-1;i>=p;i--)
ans[i+1]=a[i];
ans[p]=minn;
p=q;
for(int i=1;i<=q;i++)
a[i]=ans[i];
return ;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),b[a[i]]=i;
p=-1;
for(int i=1;i<=n;i++)
if(b[i]!=i)
{p=i;break;}
mem(ans,0);
for(int i=1;i<=p-1;i++)
ans[i]=a[i];
if(p==-1)
{print(a);continue;}
while(1)
{
if(p>n) break;
solve();
}
print(ans);
}
return 0;
}
C:
题意:一个人位于0号位置,现在要跳到n+1号位置,每次跳跃能从x跳跃到x+1~x+d。给出m块木板的长度,现在要你安排这m块木板的位置(不改变相对顺序,并且不能重叠),输出最后的方案,或者这个人不能成功到n+1点。
思路:先判断用这m个板最多能走多远,如果这个最远路程还比n小,那就输出NO,否则就是YES。然后算出最远能走的路程和n的差值,这个差值就是你可以“浪费”的步数,然后尽可能早的把这些步数浪费掉,然后下面的按照最远的走法走就行了。(最远的走法既是每块隔板间隔d-1的距离)
#include
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<0)
{
if(p>=d-1)
{p-=d-1,ans[++cnt]=d-1;continue;}
ans[++cnt]=p;
p=0;
}
print();
return 0;
}
D:
题意:给出一个0101串,现在执行一次操作为:交换两个相连位置的字符。最多执行k次操作,输出最终得到的字典序最小串。这个题可以重复操作相同位置,这一点和B不一样。
思路:实际上这个还是比较好想的。
#include
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<
E:
题意:这个学校里面有n个学生,你需要给他们分成若干的队伍,每个队伍最少3个人。每个队伍定义差异值是这个队伍最强的人和最弱的人的能力值差。现在你需要构建若干个队伍,使得差异值的总和最小。
思路:首先排序。然后每个队伍一定人数最多为5个人,因为6个人就可以拆成两队,然后两队的代价一定是比一个队伍的代价小。这一点想到就非常简单了,dp就行了,然后回溯找分配方案。
#include
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<=8)
dp[i]=min(min(dp[i-3]+a[i].v-a[i-2].v,dp[i-4]+a[i].v-a[i-3].v),dp[i-5]+a[i].v-a[i-4].v);
else if(i>=7)
dp[i]=min(dp[i-3]+a[i].v-a[i-2].v,dp[i-4]+a[i].v-a[i-3].v);
else
dp[i]=dp[i-3]+a[i].v-a[i-2].v;
}
printf("%I64d ",dp[n]);
ans=0;
for(int i=n;i>=3;i--)
{
if(i>=5&&dp[i]==dp[i-5]+a[i].v-a[i-4].v)
a[i].idd=++ans,a[i-1].idd=ans,a[i-2].idd=ans,a[i-3].idd=ans,a[i-4].idd=ans,i-=4;
else if(i>=4&&dp[i]==dp[i-4]+a[i].v-a[i-3].v)
a[i].idd=++ans,a[i-1].idd=ans,a[i-2].idd=ans,a[i-3].idd=ans,i-=3;
else if(i>=3&&dp[i]==dp[i-3]+a[i].v-a[i-2].v)
a[i].idd=++ans,a[i-1].idd=ans,a[i-2].idd=ans,i-=2;
}
printf("%d\n",ans);
sort(a+1,a+n+1,cmp2);
printf("%d",a[1].idd);
for(int i=2;i<=n;i++)
printf(" %d",a[i].idd);
puts("");
return 0;
}
F:
题意:现在给你两个字符串,你可以进行任意次操作。每次操作需要在每个字符串都选择出长度为len的一个区间,然后将这个区间的字符都进行翻转。问你进行若干次操作后,这俩字符串能变成一样的吗?
思路:看了大佬的博客,感觉很妙。
按照这个顺序进行判断:
第三个怎么理解呢?在判断1和2之后,我们得到的一定是一个排列,问题就变成你可以翻转若干次,两个排列能否相同。
我们考虑我们同时翻转相同长度的,我们排列的逆一定会发生奇偶性的变化,那么如果一开始奇偶性就不同,那么不管怎么翻转,都不会相同。
推荐参考博客:https://www.cnblogs.com/qscqesze/p/11799050.html
#include
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<1||cnt2[i]>1)
{ff++;break;}
if(ff) {printf("YES\n");continue;}
ll sum1=0,sum2=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]>a[j])
sum1++;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(b[i]>b[j])
sum2++;
if((sum1%2)==(sum2%2))
printf("YES\n");
else
puts("NO");
}
return 0;
}