终测前三题900+,终测完三题597,上了35分。总算突破了1700。。。
这次题目简单题来说偏难。A题9分钟才写出来。赛后补了D发现并不是太难。。。
A:一本书有n页,你现在在x页,你要翻到y页,一次只能翻正好d页。可以从第x页翻到第n页或者第1页(即使不够正好d页),再翻到第y页。不能翻到第y页输出-1,否则输出最少翻几次。
只需要分上面三种情况看能否整除取最小值就好了。如果都不能整除就是-1。
B:给你一个只包含'G'和'S'的串,你只能最多交换一次两个字母的位置。求最长连续G序列多长。
统计一下G的总数,维护一下每一个S前面连续多少个G,后面连续多少个G,然后O(n)扫一遍,对于S,考虑能否交换使其左右连接起来。(注意这里是两种情况。)
C:有n个学生,m个课题。给你每个学生所选的课题的编号和能力值。
你要选若干学生,组成人数相同的若干组,每一组学生所选课题相同,且任意两组的课题不能相同。求满足要求的总最大能力值。
用vector保存每个课题的学生的能力值,排序,预处理后缀最大值,枚举每一组学生的总数,然后人数够且后缀>0的课题就可以选了。比赛的时候读错题了,以为每组不超过m个人,害我调了一个小时。。。
D:给你n个整数d[i]>=1,这n个点依次编号1~n,让你构造一个图,使
1、这个图的直径最大
2、这个图连通
3、这n个点的度数都不超过其对应的d[i]
显然尽可能的直径最大,就是先把度不为1的点连成一条链,然后度为1的点可以接在两头,这样直径肯定最大。
然后多余的度为1的点连到度数还没够的点,看看能不能全连上即可。
因此用三个vector,第一个vector保存度为1的点的下标,第二个第三个都是pair,分别保存度和下标,以及相连的点的编号,然后按上面思路写就行了。注意no情况的判断。
代码:
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
using namespace std;
const int maxn=500+10;
int n,k;
vector<int>yi;
vector< pair<int,int> >duo,ans;
int cnt,tmp,flag;
int d[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&k);
if(k==1) yi.push_back(i);
else duo.push_back(mp(k,i));
}
if(!duo.size()) {puts("NO");return 0;}
tmp=duo.size()-1;
for(int i=0;i<duo.size()-1;i++)
{
ans.push_back(mp(duo[i].second,duo[i+1].second));
d[duo[i].second]++;
d[duo[i+1].second]++;
}
if(yi.size())
{
ans.push_back(mp(yi.back(),duo[0].second));
d[yi.back()]++;
d[duo[0].second]++;
yi.pop_back();
tmp++;
if(yi.size())
{
ans.push_back(mp(yi.back(),duo.back().second));
d[yi.back()]++;
d[duo.back().second]++;
yi.pop_back();
tmp++;
}
}
int i=0;
while(yi.size()&&i<duo.size())
{
if(d[duo[i].second]<duo[i].first)
{
ans.push_back(mp(yi.back(),duo[i].second));
d[duo[i].second]++;
yi.pop_back();
}
else i++;
}
if(yi.size()) {puts("NO");return 0;}
printf("YES %d\n",tmp);
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++)
{
printf("%d %d\n",ans[i].first,ans[i].second);
}
return 0;
}