当前位置:   article > 正文

AtCoder Beginner Contest 232(E,F补题)_atcoder beginner contest 232 e - rook path

atcoder beginner contest 232 e - rook path

E - Rook Path

题意:

给定大小为 H × W H×W H×W矩阵,你现在有一个车(在矩阵可以朝着一个方向走任意距离),现在起点为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),终点 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),你现在只可以恰好移动 k k k次,问从起点移动到终点的方案数为多少?

思路:

对于横和纵分别定义一个 d p dp dp方程,把一个二维的移动看成两个一维的移动,分别定义为 X ( i , 0 / 1 ) , Y ( i , 0 / 1 ) X(i,0/1),Y(i,0/1) X(i,0/1),Y(i,0/1)
X ( i , 0 / 1 ) X(i,0/1) X(i,0/1)为例,代表移动了 i i i次后,是否到达了终点的方案数(只考虑 x x x轴), 0 0 0代表未到达, 1 1 1代表到达了
那么此时状态转移方程
X ( i , 1 ) = X ( i − 1 , 0 ) X(i,1)=X(i-1,0) X(i,1)=X(i1,0)
X ( i , 0 ) = X ( i − 1 , 0 ) × ( n − 2 ) + X ( i − 1 , 1 ) × ( n − 1 ) X(i,0)=X(i-1,0)×(n-2)+X(i-1,1)×(n-1) X(i,0)=X(i1,0)×(n2)+X(i1,1)×(n1)
Y Y Y同理
最后就是遍历所有的 X ( i , 1 ) 和 Y ( k − i , 1 ) X(i,1)和Y(k-i,1) X(i,1)Y(ki,1),把两个一维的方案相乘,再乘上一个组合数 C k i C^i_k Cki,来确定这两个一维方案的移动顺序

#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
const int mod = 998244353;
#define ll long long
int n, m, k, X1, Y1, X2, Y2;
ll x[N][2], y[N][2], fact[N], infact[N];
ll qmi(int a, int k, int p) {
    ll res = 1;
    while (k) {
        if (k & 1) res = (ll)res * a % p;
        k >>= 1;
        a = (ll)a * a % p;
    }
    return res;
}
void Init() {
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i++) {
        fact[i] = (ll)fact[i - 1] * i % mod;
        infact[i] = (ll)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
}

ll C(int a, int b) {
    if (a < b) return 0;
    return fact[a] * infact[b] % mod * infact[a - b] % mod;
}

int main() {
    Init();
    scanf("%d%d%d", &n, &m, &k);
    scanf("%d%d%d%d", &X1, &Y1, &X2, &Y2);

    if (X1 != X2) {
        x[0][0] = 1;
    } else {
        x[0][1] = 1;
    }
    if (Y1 != Y2) {
        y[0][0] = 1;
    } else {
        y[0][1] = 1;
    }
    for (int i = 1; i <= k; i++) {
        (x[i][1] += x[i - 1][0]) %= mod;
        x[i][0] += (x[i - 1][1] * (n - 1) % mod + x[i - 1][0] * (n - 2) % mod) % mod;
        x[i][0] %= mod;

        (y[i][1] += y[i - 1][0]) %= mod;
        y[i][0] += (y[i - 1][1] * (m - 1) % mod + y[i - 1][0] * (m - 2) % mod) % mod;
        y[i][0] %= mod;
    }

    ll res = 0;

    for (int i = 0; i <= k; i++) {
        ll temp = x[i][1] * y[k - i][1] % mod;
        res += temp * C(k, i) % mod;
        res %= mod;
    }
    printf("%lld\n", res);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

F - Simple Operations on Sequence

题意:

给定两个长度为 N N N A A A序列和 B B B序列,现在有 2 2 2种操作
1. 1. 1.使得 A i A_i Ai增加 1 1 1或者减少 1 1 1,需要消耗 X X X
2. 2. 2.交换 A i A_i Ai A i + 1 A_{i+1} Ai+1的位置,需要消耗 Y Y Y
可以操作多次,问最少花费多少才能使 A A A序列变成 B B B序列

思路:

序列长度很小,仅为 18 18 18,所以可以考虑状态压缩
假设现在 A A A序列中的数有 i i i个已经确定,变成 B B B中的前 i i i个,当然也可以倒过来,是等效的。
那么现在如何表示 A A A中哪 i i i个数已经确定下来了,就是通过二进制,也就是状态压缩,通过二进制中的 1 1 1的位置,来表示哪些数已经被确定下来了。那么此时只需要枚举,接下来确定那个数即可,且这个数在当前状态下不能被确定过,此时枚举的就是第 i + 1 i+1 i+1个数,那么此时的代价就是 ∣ A [ i + 1 ] − B [ j ] ∣ × X + c n t × Y |A[i+1]-B[j]|×X+cnt×Y A[i+1]B[j]×X+cnt×Y c n t cnt cnt就是把 B j B_j Bj移动到 A i + 1 A_{i+1} Ai+1位置所需要交换的次数。
所以假设状态就是 s t a t e state state, c n t S cntS cntS为确定数的个数,枚举的数是 B j B_j Bj c n t cnt cnt是把
f ( s t a t e ∣ 1 < < ( j − 1 ) ) = m i n ( f ( s t a t e ∣ 1 < < ( j − 1 ) ) , f ( s t a t e ) + ∣ A [ i + 1 ] − B [ j ] ∣ × X + c n t × Y ) f(state|1<<(j-1))=min(f(state|1<<(j-1)),f(state)+|A[i+1]-B[j]|×X+cnt×Y) f(state1<<(j1))=min(f(state1<<(j1)),f(state)+A[i+1]B[j]×X+cnt×Y)
根据下标是从 0 0 0还是 1 1 1开始,调整一下下标即可

拿实例来说的话,
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int N = 1 << 18;
#define ll long long
const ll inf = 2e18;
int n;
ll a[30], b[30];
ll f[N], x, y;
bool c[30];
ll F(int state, int now) {
    int res = 0;
    for (int i = 1; i <= n; i++) {
        if ((state & (1 << (i - 1))) == 0 && i < now) res++;
    }
    return res;
}
int main() {
    scanf("%d%lld%lld", &n, &x, &y);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &b[i]);
    }
    f[0] = 0;
    for (int i = 1; i < (1 << n); i++) {
        f[i] = inf;
    }

    for (int state = 0; state < (1 << n); state++) {
        int cntS = 0;
        for (int j = 1; j <= n; j++) {
            if (state & (1 << (j - 1))) {
                cntS++;
            }
        }

        for (int j = 1; j <= n; j++) {
            if (state & (1 << (j - 1))) continue;
            f[state | (1 << (j - 1))] = min(f[state | (1 << (j - 1))], f[state] + abs(a[cntS + 1] - b[j]) * x + F(state, j) * y);
        }
    }
    printf("%lld\n", f[(1 << n) - 1]);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/74388
推荐阅读
相关标签
  

闽ICP备14008679号