目录
模拟&搜索
1A 250pts
1B 250pts
1C 250pts
2A 400pts
贪心
三分
动态规划
数学相关
题目
Round 1A
250pts
题意简述
给你一个形如HH:MM的时刻,问把时针分针倒换之后的时刻是多少。
数据范围
保证时针分针指向的是表盘的数字。
思路
直接模拟即可
代码
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
class EllysTimeMachine{
public:
int h,m,hh,mm;
string getTime(string t)
{
h=(t[0]-'0')*10+(t[1]-'0');
m=(t[3]-'0')*10+(t[4]-'0');
hh=(m/5+11)%12+1;
mm=h*5%60;
string ret;
ret+=(hh/10)+'0';
ret+=(hh%10)+'0';
ret+=':';
ret+=(mm/10)+'0';
ret+=(mm%10)+'0';
return ret;
}
};
500pts
题意简述
有
n
只袜子,并给出每只袜子的大小
数据范围
1≤n≤1000 , 1≤sizei≤109
思路
DP。
先排序。
f[i][j]
表示前
i
个匹配
f[i][j]=min(max(f[i−2][j−1],abs(s[i]−s[i−1])),f[k][j])k∈[1,i−1]
决策是是否和前一个配对。
这样复杂度是
O(n3)
难以通过。
我们可以对后面的
f[k][j]
做一个前缀min,这样复杂度
O(n2)
一开始想成了二分答案二分图匹配= =
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<algorithm>
using namespace std;
class EllysSocks{
public:
int f[1010][1010];
int mn[1010][1010];
int n;
int getDifference(vector<int> s,int p)
{
memset(f,0x3f,sizeof(f));
memset(mn,0x3f,sizeof(mn));
n=s.size();
sort(s.begin(),s.end());
for (int i=1;i<n;i++)
{
f[i][0]=mn[i][0]=0;
for (int j=1;j<=min(p,(i+1)/2);j++)
{
f[i][j]=min(f[i][j],mn[i-1][j]);
if (i-2>=1)
f[i][j]=min(f[i][j],max(abs(s[i]-s[i-1]),mn[i-2][j-1]));
else
f[i][j]=min(f[i][j],abs(s[i]-s[i-1]));
mn[i][j]=min(mn[i-1][j],f[i][j]);
}
}
return f[n-1][p];
}
};
Round 1B
250pts
题意简述
给出一个数
n
,每次可以将
数据范围
思路
略微观察之后会发现经过几次变换之后,数字会在
[1,729]
之间变化,开一个此范围的bool数组记录。
爆搜即可。
代码
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
class ExploringNumbers{
public:
bool vis[1010];
bool is_prime(int n)
{
if (n==1)
return false;
for (int i=2;i<=int(sqrt(n));i++)
if (n%i==0)
return false;
return true;
}
int dfs(int x)
{
if (is_prime(x))
return 1;
if (x<=729)
{
if (vis[x])
return -1;
vis[x]=1;
}
int tmp=0;
while (x>0)
tmp+=(x%10)*(x%10),x/=10;
tmp=dfs(tmp);
if (tmp==-1)
return -1;
else
return tmp+1;
}
int numberOfSteps(int n)
{
memset(vis,0,sizeof(vis));
return dfs(n);
}
}
500pts
题意简述
给出
n
个数
数据范围
1≤n≤50
1≤ai≤106
1≤di≤103
思路
贪心。
按位贪心,从最大的数字开始,每次选取尽量多的。
代码
#include<cstdio>
#include<vector>
using namespace std;
class ReplacingDigit{
public:
int getMaximumStockWorth(vector<int> s,vector<int> d)
{
int fac10[10],f[100][20]={0},num[20]={0},cnt[20]={0};
int n,sum=0,last;
fac10[0]=1;
for (int i=1;i<=6;i++)
fac10[i]=fac10[i-1]*10;
n=s.size();
for (int i=0;i<n;i++)
{
int tmp=s[i],di=0;
while (tmp)
f[di][tmp%10]++,tmp/=10,cnt[di]++,di++;
}
for (int i=0;i<9;i++)
num[i+1]=d[i];
for (int i=9;i>=0;i--)
{
last=cnt[i];
for (int j=9;j>=0;j--)
{
if (last-f[i][j]<=0)
{
sum+=last*j*fac10[i];
break;
}
last-=f[i][j];
sum+=f[i][j]*j*fac10[i];
if (last-num[j]<=0)
{
sum+=last*j*fac10[i];
num[j]-=last;
break;
}
last-=num[j];
sum+=num[j]*j*fac10[i];
num[j]=0;
}
}
return sum;
}
};
1000pts
题意简述
给出
n
个物品,每个物品有一个限制
再给出
h
个普通产生器,花费
还有一个特殊产生器,花费
求满足所有限制
数据范围
给出种子,数据根据给出的生成方式生成。
1≤n,h,t≤105
1≤li≤ri≤n
1≤pi≤107
思路
三分+贪心。
如果以特殊产生器的
x
为自变量,可以观察到普通产生器的费用是一个斜率单调不降的减函数,而特殊产生器的费用是一个一次函数。那么最终的费用一定是一个单峰函数。
我们可以三分这个
代码
#include<cstdlib>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
class SettingShield{
public:
struct interval{
int l,r;
}L[100010];
int n,h,t;
int seq[100010],p[100010],rightmax[100010],tag[100010];
long long calc(int x)
{
for (int i=0;i<n;i++)
p[i]=max(0,seq[i]-x);
long long sum=1LL*x*t;
int used=0;
for (int i=0;i<n;i++)
{
used-=tag[i];
tag[i]=0;
p[i]=max(0,p[i]-used);
sum+=p[i];
used+=p[i];
tag[rightmax[i]+1]+=p[i];
}
return sum;
}
long long getOptimalCost(int _n,int _h,int _t,vector<int> val0,vector<int> a,vector<int> b,vector<int> m)
{
n=_n;
h=_h;
t=_t;
int dist;
seq[0]=val0[0];
for (int i=1;i<n;i++)
seq[i]=(1LL*a[0]*seq[i-1]+b[0])%m[0];
L[0].l=val0[1];
L[0].r=val0[2];
for (int i=1;i<h;i++)
{
L[i].l=min(1LL*n-1,(1LL*a[1]*L[i-1].l+b[1])%m[1]);
dist=L[i-1].r-L[i-1].l;
L[i].r=min(1LL*n-1,L[i].l+(1LL*a[2]*dist+b[2])%m[2]);
}
memset(rightmax,0xff,sizeof(rightmax));
for (int i=0;i<h;i++)
rightmax[L[i].l]=max(rightmax[L[i].l],L[i].r);
for (int i=1;i<n;i++)
if (rightmax[i-1]>=i)
rightmax[i]=max(rightmax[i],rightmax[i-1]);
int l=0,r=1e7+10;
long long ans=1LL<<60;
for (int i=0;i<n;i++)
if (rightmax[i]==-1)
l=max(l,seq[i]);
while (l<=r)
{
int mid1=l+(r-l)/3;
int mid2=l+(r-l)*2/3;
long long tmp1=calc(mid1),tmp2=calc(mid2);
if (tmp1>=tmp2)
{
l=mid1+1;
ans=min(ans,tmp2);
}
else
{
r=mid2-1;
ans=min(ans,tmp1);
}
}
return ans;
}
};
Round 1C
250pts
题意简述
给出
特殊地,只含一个元素的数组也是加法封闭的。
判断这个数组是不是加法封闭的。
数据范围
1≤n≤50 , −50≤si≤50
思路
一堆特判,没什么可说的。
数据范围这么小,随便做。
时间复杂度
O(n)
代码
#include<vector>
#include<string>
using namespace std;
class SumFullSet{
public:
string isSumFullSet(vector<int> s)
{
int n=s.size();
string yes="closed";
string no="not closed";
if (n==1)
return yes;
int pos=0,neg=0,zer=0;
int a,b;
for (int i=0;i<n;i++)
if (s[i]==0)
zer++;
else if (s[i]>0)
pos++,a=s[i];
else
neg++,b=s[i];
if (pos>1||neg>1)
return no;
if (zer)
if (pos==1&&neg==1&&a==b)
return no;
else
return yes;
else
return no;
}
}
500pts
题意简述
给出一个只包含ABC的字符串
s
,要求A出现间隔至少为
判断是否可以将
s
重排列,使其合法,若可以,输出任意一种方案,否则输出impossible
数据范围
思路
贪心。
求出ABC当前剩余字符每种至少需要多长能放下。
每次找合法的长度最大的接到后面。
如果找不到就返回impossible
时间复杂度
O(n)
代码
#include<cstdio>
#include<string>
using namespace std;
class ThreeProgrammers{
public:
string validCodeHistory(string st)
{
string ret;
int mx,pos,len;
int num[3]={0},val[3],last[3]={-3,-3,-3};
len=st.length();
for (int i=0;i<len;i++)
num[st[i]-'A']++;
for (int i=0;i<3;i++)
val[i]=(num[i]-1)*(i+1)+1;
for (int i=0;i<len;i++)
{
mx=0,pos=-1;
for (int j=0;j<3;j++)
if (val[j]>mx&&i-last[j]>=j+1)
mx=val[j],pos=j;
if (pos==-1)
return string("impossible");
last[pos]=i;
val[pos]-=pos+1;
ret+=pos+'A';
}
return ret;
}
}
1000pts
题意简述
给出三个数
x
,
设字符串
设
求
[1,x]
中,有多少个
A∈[1,x]
的二进制表示中,长度不超过
L
的子序列,最大的所代表的十进制数
数据范围
1≤x,y≤1012
思路
无
代码
无
Round 2A
400pts
题意简述
给出含有
n
个数的数列
问这个数有没有可能是
x
。
数据范围
1≤ai≤109
1≤x≤109
思路
这题太恶心了…解释起来也好麻烦= =
进行一下模型转换,2的系数
a
写在左边,3的系数
Possible的情况:
- 如果原数组本来就有
x
,检查其他数中
a 的最小值是否 ≤xa , b 的最小值是否≤xb 。因为我们可以先对其他所有数都取gcd,最后对这个结果与 x 再取一次gcd,从而得到x 。
同理,检查其他数 a 和b 的最大值。 - 如果两个数各有一个系数和
x
的系数相等,并且它们同为最小或者同为最大。
在图中表示为两条线有交叉,并且取值在同侧。 - 如果两个数分别有一个系数满足
x ,但是它们不满足同为最小或者同为最大,但是有第三个数横跨它们… 上图好了…但是只能这个方向交,不能另一个方向…因为我们可以维护两条交叉线段的同侧不变,所以这样是对的…
其他的情况都是Impossible
感觉说的好乱…
时间复杂度 O(n3)
代码
#include<cstdlib>
#include<string>
#include<vector>
#include<iostream>
using namespace std;
#define INF 0x3f3f3f3f
class LCMGCD{
public:
struct data{
int a,b;
}d[100],need;
int n,tmp,mina,minb,maxa,maxb;
bool ok;
string isPossible(vector<int> s,int p)
{
n=s.size();
if (n==1)
if (s[0]==p)
return string("Possible");
else
return string("Impossible");
for (int i=0;i<n;i++)
{
tmp=s[i];
d[i].a=0;
while (tmp%2==0)
tmp/=2,d[i].a++;
while (tmp%3==0)
tmp/=3,d[i].b++;
}
tmp=p;
while (tmp%2==0)
tmp/=2,need.a++;
while (tmp%3==0)
tmp/=3,need.b++;
mina=minb=INF;
for (int i=0;i<n;i++)
{
if (d[i].a==need.a&&d[i].b==need.b)
{
for (int k=0;k<n;k++)
if (k!=i)
{
mina=min(mina,d[k].a);
minb=min(minb,d[k].b);
maxa=max(maxa,d[k].a);
maxb=max(maxb,d[k].b);
}
if (mina<=need.a&&minb<=need.b||maxa>=need.a&&maxb>=need.b)
return string("Possible");
}
for (int j=0;j<n;j++)
{
if (i!=j&&d[i].a==need.a&&d[j].b==need.b)
{
if (d[i].a>d[j].a&&d[i].b<d[j].b||d[i].a<d[j].a&&d[i].b>d[j].b)
return string("Possible");
if (d[i].b>d[j].b)
for (int k=0;k<n;k++)
if (k!=i&&k!=j&&d[k].a>need.a&&d[k].b<need.b)
return ("Possible");
if (d[i].b<d[j].b)
for (int k=0;k<n;k++)
if (k!=i&&k!=j&&d[k].a<need.a&&d[k].b>need.b)
return ("Possible");
}
if (i!=j&&d[i].b==need.b&&d[j].a==need.a)
{
if (d[i].a>d[j].a&&d[i].b<d[j].b||d[i].a<d[j].a&&d[i].b>d[j].b)
return string("Possible");
if (d[i].a<d[j].a)
for (int k=0;k<n;k++)
if (k!=i&&k!=j&&d[k].a>need.a&&d[k].b<need.b)
return ("Possible");
if (d[i].a>d[j].a)
for (int k=0;k<n;k++)
if (k!=i&&k!=j&&d[k].a<need.a&&d[k].b>need.b)
return ("Possible");
}
}
}
return ("Impossible");
}
};
Regional Wildcard
250pts
题意简述
给出
n
个物品,每个物品有一个价值
要求A,B两人每人至少分得一个物品,且两人分得物品的价值和相等,求任意一个方案,或无解。
数据范围
2≤n≤1000 , 1≤xi≤106
思路
生日悖论。
p
为从给定从符合离散均匀分布的区间
观察到和的值域为
[1,109]
,我们可以利用生日悖论,算出
>105>2n√
个和,看其中是否有相同的,出错概率已经非常小了。
为了进一步保证正确性,我们构造尽量多的解。可以取前25个数,算出它的所有子集的和,二进制位记录状态。
无解的情况只可能存在于
n
比较小的情况。
时间复杂度
代码
#include<cstdio>
#include<vector>
using namespace std;
int ff[25000010];
int final[1010];
bool flag;
int sz;
class DivideJewelry{
public:
void dfs(int now,int sum,int tmp,vector<int> &v)
{
if (flag)
return;
if (ff[sum]&&ff[sum]!=tmp)
{
flag=true;
for (int i=0;i<25;i++)
if (!(bool(tmp&(1<<i))^bool(ff[sum]&(1<<i))))
final[i]=0;
else if (bool(tmp&(1<<i)))
final[i]=1;
else
final[i]=-1;
return;
}
ff[sum]=tmp;
if (now==sz)
return;
dfs(now+1,sum,tmp,v);
dfs(now+1,sum+v[now],tmp|(1<<now),v);
}
vector<int> divide(vector<int> x)
{
sz=min(int(x.size()),25);
dfs(0,0,0,x);
if (!flag)
{
vector<int> ret;
return ret;
}
else
{
vector<int> ret(final,final+int(x.size()));
return ret;
}
}
};
500pts
题意简述
请你构造一个点数不超过 20 的无向图,这个无向图的联通子图个数为 k
数据范围
思路
ing..
代码
无