昨天晚上的cf,和程磊开黑打的,听说div3好上分,本来还指望这场上130分直接把颜色改了,没想到翻车了......就过了俩题,A和D过了,B题是个贪心,没写好。
A水题。
B:
题意:给你一个n的全排列,你可以操作n-1次(或者少于n-1次),问操作完后使字典序最小,输出这个字典序最小的排列。对于每种操作,你可以选一个数i(1<i<=n-1),然后交换a[i]和a[i+1],并且i不能重复选。
思路:每次把要操作的区间的最小值找出来,然后把他弄到区间的最前面,然后区间往右推移,直到结束。
这种题,我总是思路不太清晰,边码边写,弄得很乱,调不出来。还是应该先把思路弄清再写!
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
using namespace std;
int n,a[110],b[110],ans[110],p;
void print(int ans[])
{
printf("%d",ans[1]);
for(int i=2;i<=n;i++)
printf(" %d",ans[i]);
puts("");return ;
}
void solve()
{
int minn=n+1,q;
for(int i=p;i<=n;i++)
{
if(minn>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<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
using namespace std;
int n,m,d,a[1010],ans[1010],sum[1010],cnt;
void print()
{
int f=0,q=1,qq=0;
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=ans[i];j++)
{
if(f==0)
f++,printf("0"),qq++;
else
printf(" 0"),qq++;
}
for(int j=1;j<=a[q];j++)
{
if(f==0)
f++,printf("%d",q),qq++;
else
printf(" %d",q),qq++;
}
q++;
}
for(int i=q;i<=m;i++)
{
for(int j=1;j<=a[i];j++)
{
if(f==0)
f++,printf("%d",i),qq++;
else
printf(" %d",i),qq++;
}
}
for(int i=qq+1;i<=n;i++)
{
if(f==0)
f++,printf("0");
else
printf(" 0");
}
puts("");
}
int main()
{
scanf("%d%d%d",&n,&m,&d);
sum[0]=0;
for(int i=1;i<=m;i++)
scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
int S=sum[m]+(d-1)*(m+1);
if(S<n)
{printf("NO\n");return 0;}
puts("YES");
mem(ans,0);
cnt=0;
int p=n-sum[m];
while(p>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<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
using namespace std;
const int maxn=1e6+10;
ll n,k;
char s[maxn];
int a[maxn],b[maxn],ans[maxn];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%I64d%I64d",&n,&k);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
a[i]=s[i]-'0';
int cnt=0;
for(int i=1;i<=n;i++)
if(a[i]==0)
b[++cnt]=i;
if(cnt==0) {printf("%s\n",s+1);continue;}
ll sum=0,num=0;
int q=-1;
while(1)
{
if(num==cnt)
break;
if(sum+b[num+1]-num-1<=k)
{sum+=b[num+1]-num-1;num++;continue;}
else
{q=k-sum;break;}
}
for(int i=1;i<=num;i++)
ans[i]=0;
for(int i=1;i<=num;i++)
a[b[i]]=1;
int f=0;
for(int i=num+1;i<=n;i++)
{
if(a[i]==0)
f++;
if(f==0)
ans[i]=1;
else
ans[i]=a[i];
}
if(q!=-1)
{
ans[b[num+1]]=1;
ans[b[num+1]-q]=0;
}
for(int i=1;i<=n;i++)
cout<<ans[i];
puts("");
}
return 0;
}
E:
题意:这个学校里面有n个学生,你需要给他们分成若干的队伍,每个队伍最少3个人。每个队伍定义差异值是这个队伍最强的人和最弱的人的能力值差。现在你需要构建若干个队伍,使得差异值的总和最小。
思路:首先排序。然后每个队伍一定人数最多为5个人,因为6个人就可以拆成两队,然后两队的代价一定是比一个队伍的代价小。这一点想到就非常简单了,dp就行了,然后回溯找分配方案。
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const ll inf=1e17;
const int maxn=2e5+10;
using namespace std;
int n,ans;
struct node{
ll v;
int id,idd;
}a[maxn];
ll dp[maxn];
bool cmp1(node a1,node a2) {return a1.v<a2.v;}
bool cmp2(node a1,node a2) {return a1.id<a2.id;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i].v),a[i].id=i;
sort(a+1,a+n+1,cmp1);
if(n<=5)
{
printf("%d 1\n",a[n].v-a[1].v);
printf("1");
for(int i=2;i<=n;i++) printf(" 1");
puts("");return 0;
}
dp[1]=inf;dp[2]=inf;dp[3]=a[3].v-a[1].v;dp[4]=a[4].v-a[1].v;dp[5]=a[5].v-a[1].v;
for(int i=6;i<=n;i++)
{
if(i>=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的一个区间,然后将这个区间的字符都进行翻转。问你进行若干次操作后,这俩字符串能变成一样的吗?
思路:看了大佬的博客,感觉很妙。
按照这个顺序进行判断:
- 如果两个字符串存在不同的字符,那么肯定是NO
- 如果某个字符串存在两个相同的字符,那么一定是YES,因为可以就在这两个字符中进行无限次的翻转
- 如果两个字符串的逆的奇偶性相同,那么一定是YES
第三个怎么理解呢?在判断1和2之后,我们得到的一定是一个排列,问题就变成你可以翻转若干次,两个排列能否相同。
我们考虑我们同时翻转相同长度的,我们排列的逆一定会发生奇偶性的变化,那么如果一开始奇偶性就不同,那么不管怎么翻转,都不会相同。
推荐参考博客:https://www.cnblogs.com/qscqesze/p/11799050.html
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const ll inf=1e17;
const int maxn=2e5+10;
using namespace std;
int n,a[maxn],b[maxn],cnt1[110],cnt2[110];
char s1[maxn],s2[maxn];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s%s",s1+1,s2+1);
for(int i=1;i<=n;i++)
a[i]=s1[i]-'a'+1;
for(int i=1;i<=n;i++)
b[i]=s2[i]-'a'+1;
mem(cnt1,0);mem(cnt2,0);
for(int i=1;i<=n;i++)
cnt1[a[i]]++,cnt2[b[i]]++;
int f=0,ff=0;
for(int i=1;i<=26;i++)
if(cnt1[i]!=cnt2[i])
{f=1;break;}
if(f) {printf("NO\n");continue;}
for(int i=1;i<=26;i++)
if(cnt1[i]>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;
}
本文详细解析了CF Div3比赛中的六道题目,包括贪心算法、动态规划、字符串操作等,分享了作者的解题思路和代码实现。
128

被折叠的 条评论
为什么被折叠?



