首页 > 其他分享 >9.1 上午 becoder 模拟赛总结 & 题解

9.1 上午 becoder 模拟赛总结 & 题解

时间:2024-09-03 19:36:42浏览次数:11  
标签:min int 题解 拼图 becoder 方块 9.1 dp 满足要求

T1 货车运输

Kruskal 重构树模板,没什么好说的,不会的把自己重构了算了,跳过。

T2 Slagalica

可以发现拼图 1 和 2、3 拼起来还是拼图 1,拼图 4 和 2、3 拼起来也还是拼图 4,这两种拼图还都不能和自己拼,所以我们可以看作只有拼图 1 和拼图 4 来做。

对于边界拼图分别是 5、7 的情况,只有形如 414,41414 的情况可以拼进去,所以如果拼图 4 的数量不等于拼图 1 的数量 +1 就无解。

其它情况可以类推:边界是 5、8 就应该形如 41,4141;边界是 6、7 就应该形如 14,1414;边界是 6、8 就应该形如 141,14141。

考虑如何让字典序最小,很明显对于拼图 1、2、3、4,我们应该分别把四种拼图各自从小到大排序。

接下来以边界为 5、7 来举例,根据上面所说的,第一块应该放 4,而 4 前面可以放任意块 3,最后得到的拼图还是 4。

所以可以先把拼图 3 中所有小于 4 中最小值的先拼上,然后再拼上一块 4。

同理,因为接下来要拼 1,而 1 前面可以放任意块 2,所以可以先把拼图 2 中所有小于 1 中最小值的先拼上,然后再拼上一块 1。

接下来就循环到拼完就可以了,4 前面拼 3,1 前面拼 2 的这一步,可以用等同于归并排序的方式来完成。

值得注意的是拼到最后一块 4 的时候,前后要分别把 3、2 全部都给拼上去,这就是唯一有点坑的点、地方了。

边界是其它拼图的情况就以此类推了,具体可以看下面的代码。

代码写的略显臃肿,有的地方其实可以写函数的,但我比较懒,所以贴的代码看着玩就行了,时间复杂度 $O(n \log n),$(100pts):

vector<int> v[5];
int n,l,r,ls,rs,now2,now3,sz[5];
void work(int x,int y,int &i,int j){
    while(i<sz[x]&&v[x][i]<v[y][j])  printf("%d ",v[x][i++]);
    printf("%d ",v[y][j]);
}
int main(){
	scanf("%d",&n);
    for(int i=1,x,y;i<=n;i++){
        scanf("%d%d",&x,&y);
        if(x==5||x==6)  l=x,ls=y;
        if(x==7||x==8)  r=x,rs=y;
        if(x<=4)  v[x].push_back(y),sz[x]++;
    }
    for(int i=1;i<=4;i++)  sort(v[i].begin(),v[i].end());
    if(l==5&&r==7){
        if(sz[1]+1!=sz[4])  return printf("-1"),0;
        printf("%d ",ls);
        for(int i=0;i<sz[1];i++)  work(3,4,now3,i),work(2,1,now2,i);
        while(now3<sz[3])  printf("%d ",v[3][now3++]);
        printf("%d ",v[4][sz[1]]);
        while(now2<sz[2])  printf("%d ",v[2][now2++]);
    }
    if(l==5&&r==8){
        if(sz[1]!=sz[4])  return printf("-1"),0;
        printf("%d ",ls);
        for(int i=0;i<sz[1]-1;i++)  work(3,4,now3,i),work(2,1,now2,i);
        if(sz[1]){
            work(3,4,now3,sz[4]-1);
            while(now2<sz[2])  printf("%d ",v[2][now2++]);
            printf("%d ",v[1][sz[1]-1]);
            while(now3<sz[3])  printf("%d ",v[3][now3++]);
        }
        else{
            while(now3<sz[3])  printf("%d ",v[3][now3++]);
            while(now2<sz[2])  printf("%d ",v[2][now2++]);
        }
    }
    if(l==6&&r==7){
        if(sz[1]!=sz[4])  return printf("-1"),0;
        printf("%d ",ls);
        for(int i=0;i<sz[1]-1;i++)  work(2,1,now2,i),work(3,4,now3,i);
        if(sz[1]){
            work(2,1,now2,sz[1]-1);
            while(now3<sz[3])  printf("%d ",v[3][now3++]);
            printf("%d ",v[4][sz[4]-1]);
            while(now2<sz[2])  printf("%d ",v[2][now2++]);
        }
        else{
            while(now2<sz[2])  printf("%d ",v[2][now2++]);
            while(now3<sz[3])  printf("%d ",v[3][now3++]);
        }
    }
    if(l==6&&r==8){
        if(sz[1]!=sz[4]+1)  return printf("-1"),0;
        printf("%d ",ls);
        for(int i=0;i<sz[4];i++)  work(2,1,now2,i),work(3,4,now3,i);
        while(now2<sz[2])  printf("%d ",v[2][now2++]);
        printf("%d ",v[1][sz[4]]);
        while(now3<sz[3])  printf("%d ",v[3][now3++]);
    }
    printf("%d\n",rs);
}

T3 华容道

看到题目就能想到一种非常简单又能得到很多分的做法:

每个询问搜索维护空白方块和目标方块的位置,复杂度 $O(qn2m2)$,能拿到 60pts 的部分分,实际甚至更多。

接下来我们来考虑怎么去进行优化。

已知想要目标方块可以移动,空白方块就必须与它相邻,所以我们只需要维护这些状态,然后建图跑最短路就行了。

至于为什么能想到,或许是因为 ABC 平均 3 场就有一种这类型的题?

考虑怎么建图,我们先给每一种状态给个编号,编号数量的上限是只有 $4nm$ 的。

然后对于每种状态,先与两个方块移动,也就是交换位置后的状态,连一条长度为 1 的双向边。

而对于目标方块不动,只有空白方块动的情况,直接将同一目标方块位置的 4 种状态把边连起来就行了,至于边长,我们跑遍 BFS 求就行了。

这样我们最多跑 $nm$ 遍 BFS 每次复杂度也只有 $O(nm)$,所以时间是充裕的。

需要注意的是,空白方块间的连边跑 BFS 的时候不能经过目标方块,不然状态就发生改变了,如果跑不到直接不连边就行了,正确性可以手模一下。

建完图了,我们接下来只需要处理询问就行了。

对于每次询问,我们先 BFS 一下将空白方块移动到目标方块起始点四周的距离,注意和刚才一样,不能经过目标方块。

然后对于四个状态分别跑 dijkstra,然后对于每次最短路跑出来的结果,将到终点四个状态的距离取最小值就可以了,记得加上最开始空白方块的移动距离。

最后对于这种方法还用一个天坑:那就是起终点相同要直接输出 0,就不用做其它的了,至于为什么,你试一下就知道了。

其它应该就没什么需要注意的了,时间复杂度 $O(n2m2+qnm\log(nm))$,代码贴在下面(100pts):

#define N 35
bool vis[N][N],viv[N*N*5];
int n,m,q,a[N][N],dis[N*N*5];
vector<pair<int,int>> v[N*N*5];
const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int get(int x,int y,int z){return (x-1)*m*4+(y-1)*4+z;}
int bfs(int sx,int sy,int tx,int ty){
    queue<tuple<int,int,int>> q;
    while(!q.empty())  q.pop();
    q.emplace(sx,sy,0),memset(vis,0,sizeof vis),vis[sx][sy]=1;
    while(!q.empty()){
        auto [x,y,w]=q.front();q.pop();
        if(x==tx&&y==ty)  return w;
        for(int i=0;i<4;i++){
            int nx=x+dx[i],ny=y+dy[i];
            if(nx<1||nx>n||ny<1||ny>m||!a[nx][ny]||vis[nx][ny])  continue;
            q.emplace(nx,ny,w+1),vis[nx][ny]=1;
        }
    }
    return -1;
}
void dijkstra(int s){
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> q;
    while(!q.empty())  q.pop();
    memset(dis,0x3f,sizeof dis),dis[s]=0;
    memset(viv,0,sizeof viv),q.emplace(0,s);
    while(!q.empty()){
        auto [sum,num]=q.top();q.pop();
        if(viv[num])  continue;
        viv[num]=1;
        for(auto [y,w]:v[num])
            if(dis[y]>sum+w)
                q.emplace(dis[y]=sum+w,y);
    }
}
int main(){
	scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",a[i]+j);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(!a[i][j])  continue;
            a[i][j]=0;
            for(int k=0;k<4;k++){
                if(!a[i+dx[k]][j+dy[k]])  continue;
                int num1=get(i,j,k),num2=get(i+dx[k],j+dy[k],k^1);
                v[num1].emplace_back(num2,1),v[num2].emplace_back(num1,1);
                for(int u=k+1;u<4;u++){
                    if(!a[i+dx[u]][j+dy[u]])  continue;
                    int num3=get(i,j,u),sum=bfs(i+dx[k],j+dy[k],i+dx[u],j+dy[u]);
                    if(sum!=-1)  v[num1].emplace_back(num3,sum),v[num3].emplace_back(num1,sum);
                }
            }
            a[i][j]=1;
        }
    }
    for(int i=1,ex,ey,sx,sy,tx,ty;i<=q;i++){
        int ans=0x3f3f3f3f;
        scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty),a[sx][sy]=0;
        if(sx==tx&&sy==ty){a[sx][sy]=1,printf("0\n");continue;}
        for(int j=0;j<4;j++){
            if(!a[sx+dx[j]][sy+dy[j]])  continue;
            int num=get(sx,sy,j),sum=bfs(sx+dx[j],sy+dy[j],ex,ey);
            if(sum==-1)  continue;dijkstra(num);
            for(int k=0;k<4;k++)  ans=min(ans,sum+dis[get(tx,ty,k)]);
        }
        a[sx][sy]=1,printf("%d\n",ans==0x3f3f3f3f?-1:ans);
    }
}

T4 Džumbus

一眼树形 DP,看数据范围 $n \leq 1000$,一个经典的状态定义就出现了:
$dp_{i,j,0/1}$ 表示 $i$ 的子树中有 $j$ 个点满足条件,且 $i$ 满足/不满足条件时的最小花费。

接下来考虑如何转移,假设现在枚举到结点 $x$ 以及子节点 $y$。

首先,$dp_{x,i,0}$ 非常好转移,这没什么好说的对吧:

$
dp_{x,i,0}=\min_{j=1}^{j\leq \min(i,sz_y)} dp_{x,i-j,0}+\min(dp_{y,j,0},dp_{y,j,1})
$

对于 $dp_{x,i,1}$ 显然也有一个类似的不新增满足要求节点的转移:

$
dp_{x,i,1}=\min_{j=1}^{j\leq \min(i,sz_y)} dp_{x,i-j,1}+\min(dp_{y,j,0},dp_{y,j,1})
$

接下来我们考虑 $dp_{x,i,1}$ 要新增满足要求节点的转移:

第一种,$x$ 原本不满足要求,且 $y$ 满足要求,现在要让 $x$ 满足要求,因为 $y$ 已经满足要求了,所以加上 $d_x$ 的值就行了,转移式:

$
dp_{x,i,1}=\min_{j=1}^{j < \min(i,sz_y)} dp_{x,i-j-1,0}+dp_{y,j,1}+d_x)
$

第二种,$x$ 满足要求,且 $y$ 原本不满足要求,现在要让 $y$ 满足要求,因为 $x$ 已经满足要求了,所以加上 $d_y$ 的值就行了,转移式:

$
dp_{x,i,1}=\min_{j=1}^{j \leq \min(i,sz_y)} dp_{x,i-j,1}+dp_{y,j-1,0}+d_y)
$

第三种,$x$ 和 $y$ 都不满足要求,现在要让两个数都满足要求,将前两个转移方程结合一下就可以了。

$
dp_{x,i,1}=\min_{j=1}^{j < \min(i,sz_y)} dp_{x,i-j-1,0}+dp_{y,j-1,0}+d_x+d_y)
$

因为图有可能不连通,所以最后我们还要把每棵树的结果都整合起来,记 $s_i$ 表示要求 $i$ 个点满足要求的最小值,对于每次询问我们直接二分就可以了。

复杂度 $O(n^2+q \log n)$,代码贴在下面(100pts):

#define N 1005
#define LL long long
bool vis[N];
vector<int> v[N];
int n,m,q,d[N],sz[N];
LL sum[N],tmp[N],dp[N][N][2];
void dfs(int x){
    vis[x]=sz[x]=1,dp[x][0][0]=0;
    for(auto y:v[x]){
        if(vis[y]) continue;
        dfs(y),sz[x]+=sz[y];
        for(int i=sz[x];i>=0;i--){
            for(int j=min(sz[y],i);j>=0;j--){
                dp[x][i][0]=min(dp[x][i][0],dp[x][i-j][0]+min(dp[y][j][0],dp[y][j][1]));
                dp[x][i][1]=min(dp[x][i][1],dp[x][i-j][1]+min(dp[y][j][0],dp[y][j][1]));
                if(j>=1)    dp[x][i][1]=min(dp[x][i][1],dp[x][i-j][1]+dp[y][j-1][0]+d[y]);
                if(i-j>=1)  dp[x][i][1]=min(dp[x][i][1],dp[x][i-j-1][0]+dp[y][j][1]+d[x]);
                if(i-j>=1&&j>=1)  dp[x][i][1]=min(dp[x][i][1],dp[x][i-j-1][0]+dp[y][j-1][0]+d[x]+d[y]);
            }
        }
    }
}
int main(){
	scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)  scanf("%d",d+i);
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        v[x].push_back(y),v[y].push_back(x);
    }
    memset(dp,0x3f,sizeof dp);
    memset(sum,0x3f,sizeof sum),sum[0]=0;
    memset(tmp,0x3f,sizeof tmp),tmp[0]=0;
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            dfs(i);
            for(int j=1;j<=sz[i];j++)
                for(int k=n;k>=j;k--)
                    sum[k]=min(sum[k],tmp[k-j]+min(dp[i][j][0],dp[i][j][1]));
            for(int j=0;j<=n;j++)  tmp[j]=sum[j];
        }
    }
    scanf("%d",&q);
    for(int i=1,x;i<=q;i++){
        scanf("%d",&x);
        printf("%d\n",upper_bound(sum+1,sum+n+1,x)-sum-1);
    }
}

标签:min,int,题解,拼图,becoder,方块,9.1,dp,满足要求
From: https://www.cnblogs.com/tkdqmx/p/18395273

相关文章

  • 8.31 晚上 ABC369 总结 & 题解
    打了一天的比赛。ABCD太水了,直接放代码链接得了,点字母就能看对应代码。E-SightseeingTour看范围$N$只有$400$,所以我们可以先用floyd搞出任意两点间的距离。对于每次询问,发现$K_i$只有$5$,所以可以直接深搜应该走哪座桥,和应该走到哪一端。时间复杂度$O(N3+QK_i......
  • 9.2 上午 becoder 模拟赛总结 & 题解
    T1加法最开始看了好久没想出来,先做T2去了,然后回来想了一会儿发现自己可能智商有点问题。看到求最小值最大,第一反应肯定是二分,那我们怎么应该怎么check呢?考虑顺次枚举序列$A$中的每一个数,然后如果这个数没有达到mid的要求,我们肯定是要添加区间的。那么我们怎么添加区......
  • 9.3 上午 becoder 模拟赛总结 & 题解
    T1能量获取简单的树形DP,设$dp_{i,j}$表示向$i$节点传递了$j$点能量并全部花费完后能激活的封印石的数量。显然有:$ans=\sum\max_{j=0}^{j\leqW_i}{dp_{i,j}}(i\inson_0)$,转移的初始状态为$dp_{i,E_i}=E_i$。设当前枚举到的节点为$x$,子节点为$y$,有经典树上背包转......
  • 算法专项—第十五届蓝桥杯程序设计题解
    前三道属于签到题目;后面才是有难度的!一、读书一本书共n页,小明计划第一天看x页,此后每一天都要比前一天多看y页,请问小明几天可以看完这本书?输入格式一行输入三个整数n,x,y(20≤n≤5000),(1≤x,y≤20),分别表示书的总页数、计划第一天看的页数以及此后每天都要比前一天多看的页数,整......
  • 『主席树』疯狂的颜色序列 题解
    又又又好久没写题解了青花-周传雄三月走过柳絮散落恋人们匆匆我的爱情闻风不动翻阅昨日仍有温度蒙尘的心事恍恍惚惚已经隔世遗憾无法说惊觉心一缩紧紧握着青花信物信守着承诺离别总在失意中度过记忆油膏反复涂抹无法愈合的伤口你的回头划伤了沉默那夜重......
  • SP1843 LEONARDO - Leonardo Notebook 题解
    题目传送锚点博客园食用更佳前置知识1前置知识2首先是判断有无解。先疯狂寻找完所有的环,然后判断是否是偶环,最后如果都是偶环,且偶环的路径数成双成对地出现,或全是奇环,就输出Yes,不然就直接pass掉,输出No。然后我们发现:这里竟然出现了置换群!!!为什么它是置换群呢?我们从群的定......
  • P10878 [JRKSJ R9] 在相思树下 III 题解
    Description给定一个长为\(n\)的序列\(a_{1\dotsn}\),需要对它进行两种操作共\(n-1\)次。对一个长度为\(l\)的序列\(b_{1\dotsl}\)进行一次操作将会把序列变为一个长为\(l-1\)的序列\(c_{1\dotsl-1}\):操作一中,\(\foralli\in[1,l),c_i=\max(b_i,b_{i+1})\);操作......
  • 中建智地1-8月房山住宅网签29.17亿!房山国贤府成交均价4.2万立区域改善标杆
    来源:中国网中建智地1-8月房山4盘住宅网签844套、成交金额29.17亿元,成为房山住宅网签金额、套数、面积三冠王。其中学府印悦以网签359套、成交金额10.95亿元,荣膺1-8月房山住宅网签金额、套数双冠王。(1-8月房山住宅网签房企排行榜,数据来源:天朗)在刚刚过去的8月,中建智地继续冠领......
  • Capital许可管理常见问题解答
    在软件资产管理过程中,企业经常会遇到各种关于许可管理的问题。这些问题不仅影响软件的合规使用,还可能导致不必要的法律风险和成本浪费。作为专业的软件许可管理解决方案提供商,Capital致力于帮助企业轻松应对这些挑战。以下是Capital许可管理中常见的问题及其解答,助您更好地理解和......
  • 2024.9.1
    今天比赛考到了求给无向图定向使得其变为DAG的方案数,不会,丢了100分。rk2,380(100*3+80)->rk15,280(100+0+100+80)别问为什么T4没过,因为不会T2。T2题解T3link本质上是将等价的状态压缩到相当本质后的DP。T4借助转化为二分图匹配利用霍尔定理简化/贪心......