版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
大致情况
满分:
实际得分:
失误:
T1 SOJ2197
给定一个正整数 ,现在要求你选出若干个互不相同的正整数,使得它们的和不大于 ,而且每个数的因数(不包括本身)之和最大。
预处理:每个数约数和 ,时间复杂度
法1:01背包
for(int i=1;i<=n;i++){
for(int j=n;j>=i;j--){
f[j]=max(f[j],f[j-i]+sum[i]);
}
}
答案:
法2:dfs+剪枝
void dfs(int nw,int s,int cnt){
if(cnt<f[nw]) return ;
f[nw]=cnt;
if(nw+s+1>n) return ;
for(int i=s+1;i<=n-nw;i++){
dfs(nw+i,i,cnt+sum[i]);
}
}
答案:
死因
T2 SOJ2198
给一个长度为 的数组 ,给定 ,求所有满足长度大等于 ,小等于 的 数组的子区间的平均值的最大值。
做法:二分答案+单调队列
二分平均值 ,将 数组每个数减去 ,再判断是否有一段长度在 间的区间和大于 ,用单调队列判断。
bool check(double x){
for(int i=1;i<=n;i++){
b[i]=a[i]-x;
sum[i]=sum[i-1]+b[i];
}
int head=1,tail=0;
double ans=0;
for(int i=1;i<=n;i++){
while(head<=tail&&i-q[head]>R){
++head;
}
if(i>=L){
while(head<=tail&&sum[q[tail]]>sum[i-L]){
--tail;
}
q[++tail]=i-L;
ans=max(ans,sum[i]-sum[q[head]]);
}
}
return ans>0;
}
T3 SOJ2199
给定 条方向不确定的有向边,让你选出一些边并确定它们的方向,使每个点的入度刚好为 ,并使边权之和最小。
做法
容易发现如果所有边均为无向边,那么我们的图将会由至少一颗基环树组成。所以我们的目标就变为找最小的基环树森林。
我们用最小生成树的思想,从小到大加边。
在加边时,我们考虑其两个端点是否连通,如果连通且该连通块无环,那么加入这条边后一定会出现一个环。如果不连通,那么通过两边的两个连通块是否有环来判断新的连通块是否有环。
for(int i=1;i<=m;i++){
e[i].x=Read(),e[i].y=Read(),e[i].w=Read();
q.push(e[i]);
}
while(!q.empty()){
Edge u=q.top();
q.pop();
int x=findfa(u.x),y=findfa(u.y);
if(x==y){
if(!type[x]) type[x]=1,ans+=u.w,cnt++;
}
else{
if(type[x]&&type[y]) continue;
fa[x]=y; type[y]=type[y]|type[x];
ans+=u.w; cnt++;
}
}
if(cnt!=n) cout<<"No\n";
else cout<<ans<<endl;
死因
没有考虑整个图不连通的情况。