今天的题lyf大神AK了!!!而我还是两百来分… …
T1
一个简单的dfs搜索加剪枝
(我唯一一道A了的题)
#include<bits/stdc++.h>
using namespace std;
int read()
{
int i=0;char ch;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
return i;
}
int n,m,k,ans=0x3f3f3f,a[20][20],s[20],sum[20][20],v[20];
void dfs(int x,int val)
{
int tag=0;
for(int i=1;i<=m;i++)
{
if(s[i]+sum[n][i]-sum[x-1][i]<k) return;
if(s[i]<k) {tag=1;break;}
}
if(!tag) ans=min(ans,val);
else
{
dfs(x+1,val);
for(int i=1;i<=m;i++) s[i]+=a[x][i];
dfs(x+1,val+v[x]);
for(int i=1;i<=m;i++) s[i]-=a[x][i];
}
}
int main()
{
freopen("algor.in","r",stdin);
freopen("algor.out","w",stdout);
n=read();m=read();k=read();
for(int i=1;i<=n;i++)
{
v[i]=read();
for(int j=1;j<=m;j++) a[i][j]=read(),sum[i][j]=sum[i-1][j]+a[i][j];
}
for(int i=1;i<=m;i++) if(sum[n][i]<k) {printf("-1");return 0;}
dfs(1,0);
printf("%d",ans);
return 0;
}
T2
一道思维题。
直接分解每个数的质因子暴力求解会T到飞起,只能过一个点。(我就是这么做的,只有10pts)
我们可以这样考虑:
对于一个数x,它的倍数一定含有x因子;
如果
(
m
+
1
)
∗
x
>
n
(m+1)*x>n
(m+1)∗x>n 且
m
x
≤
n
mx\le n
mx≤n ,则 x 这个因子一定会加 m 次。
所以我们只需要枚举n中的每个数
i
i
i ,将它在
[
1
,
n
]
[1,n]
[1,n]内的倍数全部加起来得到
S
i
S_i
Si,再将
S
i
S_i
Si求和得到的就是答案。
#include<bits/stdc++.h>
using namespace std;
int n;long long res;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i) res+=j;//j循环可以用等差数列求和O(1)处理
printf("%lld",res);
return 0;
}
(std代码贼短,而我的巨长还T了… …)
T3
一道贪心题
考场上我是按物品的价值从大到小排序,依次枚举每个物品,若该时间没有放过物品,则放下,若已经有东西了,就向前移动,直到走到一个空的时间,若前面没有空的时间了,就放弃这个物品。
想法是正确的,但时间复杂度不允许(
n
≤
1
e
5
n\le1e5
n≤1e5),我想优化,结果打挂了… …
所以我们就需要一个
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的优秀贪心。
我们可以按时间排序从小到大,不断枚举,若枚举个数超过当前最大时间,则踢掉最小的那个数,要用小根堆维护。
#include<bits/stdc++.h>
using namespace std;
int read()
{
int i=0;char ch;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
return i;
}
const int N=1e5+5;
struct node{
int t,v;
}a[N];
int n,ans,pos;
priority_queue<int,vector<int>,greater<int> >q;
bool cmp(const node &a,const node &b) {return a.t<b.t;}
int main()
{
freopen("mouse.in","r",stdin);
freopen("mouse.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i].t=read();
for(int i=1;i<=n;i++) a[i].v=read();
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(pos<a[i].t)
{
pos++;
q.push(a[i].v);
ans+=a[i].v;
continue;
}
int u=q.top();
if(u<a[i].v)
{
q.pop();
ans=ans-u+a[i].v;
q.push(a[i].v);
}
}
printf("%d",ans);
return 0;
}
T4
求树上最长递增子序列。
直接dfs一遍,对每个节点
n
l
o
g
n
nlogn
nlogn 求一次最长递增子序列,回溯记录答案。
考场上我不知道怎么想的,在树上遍历了n遍,又双叒T了… …
#include<bits/stdc++.h>
using namespace std;
int read()
{
int i=0;char ch;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
return i;
}
const int N=2e5+5;
int n,a[N],tot,ans,mx[N],nxt[4*N],to[4*N],first[N],b[N];
void add(int x,int y)
{
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void dfs(int u,int fa)
{
int an,flag,j;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(v==fa) continue;
an=ans;flag=0;
if(b[ans]<a[v]) b[++ans]=a[v];
else
{
j=lower_bound(b+1,b+ans+1,a[v])-b;
flag=b[j];
b[j]=a[v];
}
mx[v]=ans;
dfs(v,u);
ans=an;
if(flag) b[j]=flag;
}
}
int main()
{
freopen("treelis.in","r",stdin);
freopen("treelis.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
ans=1;b[1]=a[1];mx[1]=1;
dfs(1,0);
for(int i=1;i<=n;i++) printf("%d\n",mx[i]);
return 0;
}
T5
考场上我做的80分做法,暴力枚举两个点,判断是否有边,最后统计联通块个数,不知道为什么还WA了一个点… …
因为
A
≤
B
≤
1
e
5
A\le B\le1e5
A≤B≤1e5,所以每个数最多拥有6个质因数(235711*13=30030),因此我们可以线性筛出
[
1
,
B
]
[1,B]
[1,B]内的所有质数,然后枚举它们在
[
A
,
B
]
[A,B]
[A,B]之间的倍数,将这些数放进一个联通块,最后统计联通块个数即可。
#include<bits/stdc++.h>
using namespace std;
int read()
{
int i=0;char ch;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
return i;
}
const int N=1e5+5;
int fa[N],cnt,c[N],P,n,m,h[N],ans,tag[N];
int getfa(int x) {return x==fa[x] ? x : fa[x]=getfa(fa[x]);}
int main()
{
freopen("set.in","r",stdin);
n=read();m=read();P=read();
for(int i=n;i<=m;i++) fa[i]=i;
for(int i=2;i<=m;i++)
{
if(tag[i]) continue;
c[++cnt]=i;
for(int j=i;j<=m;j+=i)
tag[j]=1;
}
for(int i=cnt;i>=1;i--)
{
if(c[i]<P) break;
int t=n/c[i]*c[i];
while(t<n) t+=c[i];
int o=t;
while(t<=m)
{
fa[getfa(t)]=getfa(o);
t+=c[i];
}
}
for(int i=n;i<=m;i++)
{
int t=getfa(i);
if(!h[t]) {h[t]=1;ans++;}
}
printf("%d",ans);
return 0;
}
总结
这次考试码量都不大,但考得也不是很好,T2问题没有转换成功,T3思维没有打开,被之前考试的一道题局限,T4想复杂了,T5没时间思考,下次得有所改进。