ABC224做题笔记

Atcoder Begineer Contest 224

D - 8 Puzzle on Graph

题目大意

给定一个 9 个顶点,m 条边的图,共有八个棋子分别在 p1,p2,p3...p8,问最终能否让第 i 个棋子放在 i 号节点上。

解题思路

考虑与八数码相同的做法。

将九个顶点对应的状态压缩成一个九位数,即每个定点上对应了哪个棋子。

令第 i 个定点上最开始的棋子编号是 ri,没有棋子的顶点 uru9,就能够生成初始状态。

然后将这个九位数压入队列,跑 BFS 求解,再用一个 map 记录,最后判断能否达到 123456789 这个状态。

生成初始状态部分:

string str="999999999"; //一开始都没有摆棋子
for(int i=1,pos;i<=8;i++){
    cin>>pos;
    str[pos-1]=i+'0'; //读入第 i 个棋子的位置后替换相应位置
}

BFS 部分:

mp[str]=1;
q.push(str);

while(!q.empty()){
    str=q.front();
    q.pop();

    int u=1;
    for(int i=0;i<9;i++){
        if(str[i]=='9'){
            u+=i; //找到没有棋子的顶点的位置
            break;
        }
    }

    for(auto v:G[u]){
        string n_str=str;
        swap(n_str[u-1],n_str[v-1]);
        
        if(mp[n_str]){
            continue;
        }

        mp[n_str]=mp[str]+1;
        q.push(n_str);
    }
}

if(!mp["123456789"]) cout<<-1<<"\n"; //如果没有走过,就说明无法到达
else cout<<mp["123456789"]-1<<"\n";

E - Integers on Grid

解题思路

考虑设 rmaxi 表示第 i 列的最大值,cmaxi 表示第 i 行的最大值。

对数组进行排序,然后进行状态转移:

fi=max{rmaxri,cmaxci}

rmaxi=max{rmaxri,fi+1}

cmaxi=max{cmaxci,fi+1}

答案依次输出 fi 即可。

cin>>h>>w>>n;
for(int i=1;i<=n;i++){
    cin>>r[i]>>c[i]>>a[i];
    mp[INF-a[i]].push_back(i);
}

for(auto p:mp){
    vector<int> now=p.second;

    for(auto v:now){
        f[v]=max(f[v],max(rmax[r[v]],cmax[c[v]]));
    }
    for(auto v:now){
        rmax[r[v]]=max(rmax[r[v]],f[v]+1);
        cmax[c[v]]=max(cmax[c[v]],f[v]+1);
    }
}    

for(int i=1;i<=n;i++){
    cout<<f[i]<<"\n";
}

F - Problem where +s Separate Digits

题目大意

有一个只含 19 的长度为 N 数字串。

在这个串中加最少 0 个,最多 N1 个加号,使得其变成一个加法算式,求所有可能的算式的结果的总和模 998244353

解题思路

考虑 dp

  • fi 表示 1i 中,第 i 位与第 i1 位之间没有加号的方案数

  • gi 表示 1i 中,第 i 位与第 i1 位之间有加号的方案数

容易得到状态转移:

  • fi=si×2i1+fi1×10

解释:第 i 位与第 i1 位之间没有加号,即它们是连着的一个数,因此需要先将 fi1×10,再将第 i 位的数字加上去,共有 2i1 种可能,因此就要加上 si×2i1

  • gi=gi1×2+fi1

解释:第 i 位将第 i1 位没有分离的分离开来了,因此要加上 fi1,同时之前就已经分离的会出现两个分支因此还要加上 gi1×2

最终答案为 fn+gn,时间复杂度 O(N)1N2×105,轻松通过。

n=s.length();
pow_2[0]=1ll;
for(int i=1;i<=n;i++){
    pow_2[i]=(pow_2[i-1]*2ll)%mod;
}


for(int i=1;i<=n;i++){
    ll u=s[i-1]-'0';
    f[i]=((f[i-1]*10)%mod+(u*pow_2[i-1])%mod)%mod;
    g[i]=((g[i-1]*2)%mod+f[i-1])%mod;
}

cout<<(f[n]+g[n])%mod<<"\n";

G - Roll or Increment

题目大意

有一个 N 面骰子,最初,骰子 S 面朝上。

可以进行以下两次操作,次数不限。

  • 操作一:花费 A 代价,使骰子显示的数值 "增加" 1

  • 操作二:花费 B 代价,重新投掷骰子。

要让骰子从初始状态显示 S 变为显示 T

求当使用最优策略使该期望值最小时,这样做所需花费的最小期望值

解题思路

显然,应该操作一还是操作二,只取决于骰子当前显示的数字

对于每一个 i=1,2,,N,试求 "当骰子显示 i 时,我们应该选择哪个选项"。

注意到:

  • 如果骰子显示的数字大于 T,那么显然操作二是最佳选择。

  • 操作一之后立即操作二显然比仅仅操作二更劣。

因此,在最优策略中,如果当骰子显示的数字为 P 时选择操作一,那么当骰子显示的数字为 P 使得 PP 时,应该选择操作一

根据上述两点,开始思考最优策略:

存在一个满足 1XT 的整数 X

如果骰子显示 TXT 之间的任意一个数,则选择 操作一

否则,选择 操作二

问题转化为:

求选择一个整数 X ,使得 1XT ,从而使得 "将骰子所示的骰子从 S 改为 T 的期望值"最小。

开始分类讨论:

  • 在" TX+1ST 成立 "的条件下选择 X
  • 在" TX+1ST 不成立 "的条件下选择 X

第一种情况:在 "成立 TX+1ST " 的条件下选择 X 时。

因为我们选择 操作一,直到骰子显示的数字从 S 变为 T ,所以所需的代价是 A(TS) ,与 X 无关。

第二种情况:在" TX+1ST 不成立 "的条件下选择 X 时。

骰子上的数字从 ST 的转换过程包括以下两个步骤。

  • 首先,重复操作二,直到骰子显示的数字在 TX+1T 之间。
  • 然后,重复"增加骰子显示的数字",直到数字变成 T

每一步所需的花费如下。

  1. 进行一次操作二后,骰子显示 TX+1T 之间数字的概率 "为 X/N,因此第一步中操作二的预期次数为 N/X第一步的花费为 BN/X

  2. 第一步结束后,模具有可能出现介于 TX+1T 之间的任何整数。因此,第二步的花费为 {i=TX+1TA(Ti)}/X=A(X1)/2

将骰子显示的数字从 S 变为 T 所需的花费为 A(X1)/2+BN/X

那么如何在 TX+1ST 不成立 "的条件下,求出选择整数 XTX+1ST 的最小值呢?

考虑函数 f(x)=A(x1)/2+BN/x 定义为正实数 x

可以观察图像计算可得,函数 f(x)x=2BN/A 处最小。

另外,由于 f(x)=2BN/x3 , f(x) 是一个凸函数。

因此,只需计算 f(X)2BN/A 附近的若干个 X ,使得 "不成立 TX+1ST "和 1XT

时间复杂度 O(1)

posted @   SunburstFan1106  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
点击右上角即可分享
微信分享提示