第十四届程序设计竞赛 D H
问题 D: Rorororobot
比赛时没看懂题目,错失良机
题意:问从一点x是否能到另一点y,约束每次走k格,且不能出界和碰墙
算法:st表
因为判断不能比较简单,所以先判断NO
1.如果坐标相差不满足k的倍数一定不能过
2.如果两则之间隔了一道墙,需要判断是否可行
2.1 算出两者之间最高的墙时间复杂度太高,考虑st表,o(1)直接算出
2.2 判断绕行的最高点是否满足k的倍数
神奇的代码
int a[N];
int f[N][20];
int n,m;
//st表模板
void get_f(){
for(int i=1;i<=m;i++) f[i][0]=a[i];
for(int j=1;j<=20;j++){
for(int i=1;i+(1<<j)-1<=m;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
}
void bu_f(){
n=read(),m=read();
for(int i=1;i<=m;i++) a[i]=read();
get_f();//st表
int q=read();
while(q--){
int xx1=read(),yy1=read(),xx2=read(),yy2=read(),k=read();
//两者距离要相差k
if(abs(xx1-xx2)%k||abs(yy1-yy2)%k){
cout<<"NO\n";
continue;
}
//交换,方便计算
if(yy1>yy2) {
swap(yy1,yy2);
swap(xx1,xx2);
}
//o(1)求区间最大值
int k1=log2(yy2-yy1+1);
int mx=max(f[yy1][k1],f[yy2-(1<<k1)+1][k1]);
//判断,如果过不去直接退出
if((mx==n)||(a[yy1]==xx1)||(a[yy2]==xx2)){
cout<<"NO\n";
continue;
}
//需要绕行
if(mx>xx1&&mx>xx2){
int f=0;
//判断是否可以绕行,从天花板开始,枚举是否可行
for(int i=n;i>mx;i--){
if(abs(i-xx1)%k==0&&(abs(i-xx2)%k==0)){
f=1;
break;
}
}
if(f){
cout<<"YES\n";
}
else cout<<"NO\n";
continue;
}
//其他直接过
cout<<"YES\n";
}
}
问题 H: 浙外办会展
此题对时间复杂度的要求好高,数组大小n*k(1e7)和时间复杂度n * k * logk(1e7-1e8)有点极限
思路:
题意从k种选s种,问最小代价
1.我们可以开二维数组ans[n][k],表示每个点i选种类是j的最小代价
2.这个可以bfs,算完之后,只需要sort从小到大,累加求和即可
如果你想到了这里,很遗憾,超时8%
分析,每个点都遍历一下,时间复杂度是n^2 肯定过不了;
考虑优化
我们发现很多点的种类是一样的,相当于多个源点向外扩散,每个点的最小值是距离源点最近的那个点,没错可以利用上次学到的优先队列维护最小值,时间复杂的为n* logn,可以。
一点点细节
int a[N];
int n,m,s,k;
vector<int> g[N];
int ans[N][105];
int vis[N][105];
struct node{
int dis,id,idx;//距离,国家,种类
bool operator < (const node &b)const{
return dis>b.dis;
}
};
priority_queue<node> q;
int bfs(){
while(q.size()){
auto t=q.top();
q.pop();
int dis=t.dis,id=t.id,idx=t.idx;
if(vis[id][idx]) continue;
vis[id][idx]=1;
for(int v:g[id]){
if(ans[id][idx]+1<ans[v][idx]){
ans[v][idx]=ans[id][idx]+1;
q.push({ans[v][idx],v,idx});
}
}
}
}
void bu_f(){
n=read(),m=read(),k=read(),s=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
g[x].push_back(y);
g[y].push_back(x);
}
memset(ans,INF,sizeof ans);
for(int i=1;i<=n;i++){
ans[i][a[i]]=0;//源点为0
q.push({0,i,a[i]});//压入队列
}
bfs();
for(int i=1;i<=n;i++){
sort(ans[i]+1,ans[i]+k+1);//排序
ll sum=0;
for(int j=1;j<=s;j++){
sum+=ans[i][j];//累加求和
}
write(sum);
printf(" ");
}
}