简单题
直接枚举−3000到3000也没关系。
枚举最后的x,容易发现x 的取值不会在序列取值之外。
不开long long 会挂掉30分。
#include<bits/stdc++.h>
#define inf pow(2,64)
using namespace std;
long long n,a[3500];
long long mans=-inf,mians=inf;
unsigned long long ans=inf;
inline int read()
{
int f=1;
int res=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
return res*f;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read(),mans=max(mans,a[i]),mians=min(mians,a[i]);
for(int i=mians;i<=mans;i++)
{
unsigned long long tot=0;
for(int j=1;j<=n;j++)
tot+=(i-a[j])*(i-a[j]);
ans=min(ans,tot);
}
cout<<ans;
return 0;
}
移动杠杆
题目大意
两排数,你的目标是将数值相同的放到一起。
滚动不消耗代价。
提起消耗的代价为数值。
你需要最小化提起的数值限制,也就是移动的最大值尽可能小
30分算法:
一旦一个数值使用了第二种操作,可以直接把它看成消失了,因为可以不对剩下的数造成任何阻碍。
枚举每个数是否使用第二种操作,然后 check 一下剩下的数对中间是否都没有障碍数。
时间复杂度O(a2n),期望得分30。
但是你只要想出了第一个方法,你一般也就不会只有30分,但这都是后话。
60~80分算法:
考虑一对数,如果它们跨排,那么一定会使用第二种操作。
如果在同排,达到目标有两种方案:自身使用第二个操作或是中间的所有数都使用了第二个操作。
• 问题转化为了区间取max。直接循环扫,期望得分60∼80。
100分算法1:
使用一些方法优化到O(nlogn) 就可以获得满分。
#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
inline int read()
{
int f=1;
int res=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
return res*f;
}
int n,nans[maxn],top;
int a[maxn],b[maxn];
int ans;
int A[maxn],B[maxn];
inline bool check(int x)
{
int t1=0,t2=0;
for(int i=1;i<=n;i++)if(a[i]>x)A[++t1]=a[i];
for(int i=1;i<=n;i++)if(b[i]>x)B[++t2]=b[i];
if(t1==1||t2==1)return 0;
for(int i=2;i<=t1;i++)if(A[i]!=A[i-1]&&A[i]!=A[i+1])return 0;
for(int i=2;i<=t2;i++)if(B[i]!=B[i-1]&&B[i]!=B[i+1])return 0;
return 1;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
// mans=max(mans,a[i]);
//mians=min(mians,a[i]);
}
for(int i=1;i<=n;i++)
{
b[i]=read();
// mans=max(mans,b[i]);
// mians=min(mians,b[i]);
}
long long l=0,r=1e9;
while(l<=r)//二分
{
int mid=(l+r)>>1;
if(check(mid))
{
r=mid-1;
ans=mid;
}
else l=mid+1;
}
cout<<ans;
return 0;
}
串串
40分算法:
一看就是dp
• 令fi表示以 i 结尾的前缀的答案。枚举j<i,如果sj+1 ∼ i是好的字符串那么就用fj+1更新fi。
• O(n)级别的 check 期望得分40。
60~100分算法:
• 从后往前枚举j,边枚举边维护,可以做到总复杂度O(n2)。加点剪枝,期望得分60∼100。
#include<bits/stdc++.h>
#define inf 0x7fffffff
#define maxn 200005
using namespace std;
inline int read()
{
int f=1;
int res=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
return res*f;
}
int n,vis[35],dp[maxn],cont[maxn];
string s;
int main()
{
n=read();cin>>s;
memset(dp,127,sizeof(dp));
s=" "+s;
int len=s.size();
dp[0]=0;
for(int i=1;i<len;i++)
{
int sum=0;
for(int j=1;j<=26;j++)cont[j]=0;
for(int j=i;j>=0;j--)
{
if(cont[s[j]-'a'+1]==0)sum++;
cont[s[j]-'a'+1]++;
if(sum==n)dp[i]=min(dp[j-1]+1,dp[i]);
if(sum>n)break;
}
if(dp[i]>1e6)printf("%d\n",-1);
else printf("%d\n",dp[i]);
}
return 0;
}
天天
30分算法:
直接暴力枚举所有可能的 01 串,并计算它与每个已有 01 串的距离,期望得分30。
基于原来30分的算法。
对于20% 的特殊性质数据部分,直接输出1 2 即可。
100分算法:
将题目看成是一张 2m 个点的图,读入的 n−1 个 01 串是起点。
每个点都与与自己只差一位的点有连边,边权为1。
要找 dist 最大的点,并输出个数。
直接 bfs 即可获得满分。
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
return res*f;
}
int ans,tot;
string s;
int n,m,vis[100010],dp[100010];
queue<int>q;
int main()
{
n=read();m=read();
for(int i=1;i<n;i++)
{
int k=0;
cin>>s;
for(int j=0;j<m;j++)
k=(k<<1)+(s[j]&15);
if(!vis[k])q.push(k);
vis[k]=1;
}
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=0;i<m;i++)
{
int ne=x^(1<<i);
if(!vis[ne])
{
q.push(ne);
vis[ne]=1;
dp[ne]=max(dp[ne],dp[x]+1);
if(dp[ne]>ans)ans=dp[ne],tot=1;
else if(dp[ne]==ans)tot++;
}
}
}
cout<<m-ans<<' '<<tot<<endl;
return 0;
}
文献参考:参考