【无标题】AtCoder Beginner Contest 224(A-F)

AtCoder Beginner Contest 224(A-F)

知识点整理:

题号知识点备注
A
B
C
D基础DP矩阵最大路径和变种
E状态机DP
F状压DP
G?
H?

A - QQ solver

题意

给你三个字符组成的乘法表达式, 求计算结果

题解

用scanf读入整数

#include <bits/stdc++.h>

using namespace std;

int main() {
  int a, b;
  scanf("%dx%d", &a, &b);
  printf("%d\n", a*b);return 0;
}

B - Caesar Cipher

题意

给定两个字符串 A A A B B B, 问能否通过字母的轮换把A变成B

题解

一共就26种情况, 暴力枚举, 需注意char的最大范围是128, 有可能溢出.

#include <bits/stdc++.h>

using namespace std;

int main() {
    string a, b;
    cin >> a >> b;

    bool flag = false;

    for (int i = 0; i < 26; i++) {
        string c = a;
        for (int j = 0; j < c.size(); j++) {
            c[j] = (char)(a[j] + i);
            if (c[j] > 'z' || c[j] < 0) {
                c[j] = (char) (a[j] - 26 + i);
            }
        }

        if (c == b) {
            flag = true;
            break;
        }
    }
    puts(flag ? "Yes" : "No");
    return 0;
}

C - Graph Isomorphism

题意

两个图是否同构

题解

8个点, 一共就8!种情况, 暴力枚举

#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int a[N][N];
int b[N][N];
struct node {
    int x, y;
} c[N];
int n, m;
int vis[N];
int res[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        a[x][y] = 1;
        a[y][x] = 1;
        c[i] = {x, y};
    }
    for (int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        b[x][y] = 1;
        b[y][x] = 1;
    }
    bool flag = 0;
    int cnt = 0;
    int dex = 1;
    for (int i = 1; i <= m; i++) {
        if (b[res[c[i].x]][res[c[i].y]] == 0 &&
            b[res[c[i].y]][res[c[i].x]] == 0) {
            dex = 0;
            break;
        }
    }
    if(dex==1) {
        puts("Yes");
        return 0;
    }
    while (next_permutation(res + 1, res + n + 1)) {
        int now = 1;
        for (int i = 1; i <= m; i++) {
            if (b[res[c[i].x]][res[c[i].y]] == 0 &&
                b[res[c[i].y]][res[c[i].x]] == 0) {
                now = 0;
                break;
            }
        }
        if (now == 1) {
            flag = 1;
            break;
        }
    }

    if (flag)
        puts("Yes");
    else
        puts("No");
}

D - Weak Takahashi

题意

给定一个矩阵 由 . . . # \# # 两种符号组成, 点代表豆子, #号代表一堵墙, 遇到墙就不能走了, 问从左上角出发, 至多吃到几个豆子?

题解

定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示走到 ( i , j ) (i, j) (i,j)点可以吃到多少豆子, 那么可以从上边或左边转移,取较大的情况,并维护下整个dp数组的最大值即可

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 110;

int h, w;
string c[MAXN];

int dp[MAXN][MAXN];

int main() {
    cin >> h >> w;
    for (int i = 1; i <= h ; i++) {
        cin >> c[i];
        c[i] = '$' + c[i];
    }

    if (c[1][1] == '.') dp[1][1]  = 1;

    int mx = dp[1][1];
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            if (i == 1 && j == 1) continue;
            if (i > 1 && c[i-1][j] == '.') dp[i][j] = max(dp[i][j], dp[i-1][j]);
            if (j > 1 && c[i][j-1] == '.') dp[i][j] = max(dp[i][j], dp[i][j-1]);

            if (dp[i][j] && c[i][j] == '.') dp[i][j] ++;

            mx = max(mx, dp[i][j]);
        }
    }


    cout << mx<< endl;
}

E - Rook Path

题意

有一个KaTeX parse error: Undefined control sequence: \cross at position 2: H\̲c̲r̲o̲s̲s̲ ̲W的棋盘, 一个车从 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) k k k步到 ( x 2 , y 2 ) (x_2, y_2) (x2,y2)有多少种方案?

题解

一开始的思路是dp, 定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示走 i i i步到 ( j , k ) (j, k) (j,k)点的方案数, 那么可以得到转移方程:

d p [ i ] [ j ] [ k ] = ∑ 1 ≤ s ≤ W & & s ! = j d p [ i − 1 ] [ s ] [ k ] + ∑ 1 ≤ s ≤ H & & s ! = k d p [ i − 1 ] [ j ] [ s ] dp[i][j][k]=\sum_{1\le s \le W \&\& s!=j} dp[i-1][s][k] + \sum_{1\le s \le H \&\& s!=k} dp[i-1][j][s] dp[i][j][k]=1sW&&s!=jdp[i1][s][k]+1sH&&s!=kdp[i1][j][s]
这样看来, 时间和空间复杂度都高达 O ( H W K ) O(HWK) O(HWK), 显然不合要求, 考虑到大部分情况是一样的, 只跟是不是同行、同列有关, 因此把棋盘分成四种情况, 并分别用1 2 3 4表示:

1-跟终点同行 2-跟终点同列 3-等于终点 4-其他

那么就可以统计不同状态转移的方案数了:

d p [ i ] [ 1 ] = d p [ i − 1 ] [ 1 ] ∗ ( w − 2 ) + d p [ i − 1 ] [ 3 ] + d p [ i − 1 ] [ 4 ] ∗ ( w − 1 ) dp[i][1] = dp[i-1][1]*(w-2)+dp[i-1][3]+dp[i-1][4]*(w-1) dp[i][1]=dp[i1][1](w2)+dp[i1][3]+dp[i1][4](w1)

d p [ i ] [ 2 ] = d p [ i − 1 ] [ 2 ] ∗ ( h − 2 ) + d p [ i − 1 ] [ 3 ] + d p [ i − 1 ] [ 4 ] ∗ ( h − 1 ) dp[i][2] = dp[i-1][2]*(h-2)+dp[i-1][3]+dp[i-1][4]*(h-1) dp[i][2]=dp[i1][2](h2)+dp[i1][3]+dp[i1][4](h1)

d p [ i ] [ 3 ] = d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 2 ] dp[i][3] = dp[i-1][1]+dp[i-1][2] dp[i][3]=dp[i1][1]+dp[i1][2]

d p [ i ] [ 4 ] = d p [ i − 1 ] [ 1 ] ∗ ( h − 1 ) + d p [ i − 1 ] [ 2 ] ∗ ( w − 1 ) + d p [ i − 1 ] [ 3 ] ∗ ( h + w − 4 ) dp[i][4] = dp[i-1][1]*(h-1)+dp[i-1][2]*(w-1)+dp[i-1][3]*(h+w-4) dp[i][4]=dp[i1][1](h1)+dp[i1][2](w1)+dp[i1][3](h+w4)

以第一行为例解释下:

状态1 可以通过状态1, 3, 4转移来, 固定状态1点集中的一个点:

  • 状态1转移到状态1: 只要不是相同点就能转移来, 有 w − 2 w-2 w2种情况
  • 状态3转移到状态1: 只能走一种情况
  • 状态4转移到状态1: 有 w − 1 w-1 w1种情况可以转移来

其他转移关系同理., 最后答案是 d p [ k ] [ 3 ] dp[k][3] dp[k][3]

#include <bits/stdc++.h>

typedef long long LL;

#define int long long

using namespace std;

const int MAXL = 1 << 22;
char i_str[MAXL], o_str[MAXL], *i_s, *i_t;
int o_t;
#define Flush fwrite(o_str,1,o_t,stdout), o_t = 0
#define space pc(' ')
#define enter pc('\n')

inline char gc() {
    if (i_s == i_t) {
        i_s = i_str;
        i_t = i_s + fread(i_str, 1, MAXL, stdin);
        return i_s == i_t ? EOF : *i_s++;
    } else
        return *i_s++;
}

inline int read() {
    int x = 0, f = 0;
    char ch = gc();
    for (; ch < '0' || ch > '9'; ch = gc())
        if (ch == '-')
            f = 1;
    for (; ch >= '0' && ch <= '9'; ch = gc())
        x = x * 10 + (ch ^ 48);
    return f ? -x : x;
}

inline void pc(char x) {
    o_str[o_t++] = x;
    if (o_t == MAXL)
        Flush;
}

void write(int x) {
    if (x < 0)
        pc('-'), x = -x;
    if (x > 9)
        write(x / 10);
    pc(x % 10 ^ 48);
}

const int N = 1e6 + 6, Mod = 998244353;

int n, m, k, a, b, c, d, f[N][4];

signed main() {
	n = read(), m = read(), k = read();
	a = read(), b = read(), c = read(), d = read();
	if (a != c && b != d) 
		f[0][0] = 1;
	if (a == c && b != d)
		f[0][1] = 1;	
	if (a != c && b == d)
		f[0][2] = 1;
	if (a == c && b == d)
		f[0][3] = 1;
	for (register int i = 1; i <= k; ++i) {
		f[i][0] = ((f[i - 1][1] * (n - 1) % Mod + f[i - 1][2] * (m - 1)) % Mod + f[i - 1][0] * (n + m - 4) % Mod) % Mod;
		f[i][1] = ((f[i - 1][0] + f[i - 1][3] * (m - 1) % Mod) % Mod + f[i - 1][1] * (m - 2) % Mod) % Mod;
		f[i][2] = ((f[i - 1][0] + f[i - 1][3] * (n - 1) % Mod) % Mod + f[i - 1][2] * (n - 2) % Mod) % Mod;
		f[i][3] = (f[i - 1][1] + f[i - 1][2]) % Mod;
	}
	write(f[k][3]), enter ;
	Flush ;
	return 0;
}

F - Simple Operations on Sequence

题意

给你两个长度为 N N N的序列 A , B A,B A,B, 有两种操作:

  • X X X日元把 A i A_i Ai加1或减1
  • Y Y Y日元交换 A i , A i + 1 A_i, A_{i+1} Ai,Ai+1

问至少花多少钱把 A A A变成 B B B?

题解

N ≤ 18 N\le 18 N18, 所以可以考虑 状态压缩, 那么难点就在如何定义状态

本题的状态定义为:

A A A中的某 i i i个数变成 B B B的前 i i i个数的最小花销, 这个其实挺难想到的, 通常会想的是变成对应的.

那么 A A A中哪i个数, 就看状态值中1的位置在哪了, 然后看转移关系.

假设当前状态是 d p [ S ] dp[S] dp[S], 其中 S S S中有i个1, (这个值在C++中有一个函数__builtin_popcount 可以快速计算出来), 那么我们需要把某个数换到 B i + 1 B_{i+1} Bi+1上, 这个数在 S S S中对应的状态一定是0, 我们枚举这个为0的位置 j j j, 进行两步操作, 先把 A j A_j Aj变成 B i + 1 B_{i+1} Bi+1, 再换到 i + 1 i+1 i+1位上, 计算花多少钱, 维护最小值.

那么接下来要解决的是**把 A j A_j Aj变成 B i + 1 B_{i+1} Bi+1 **要操作多少次, 这个次数等价于j右面有多少个0, 因为前面为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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值