赞
踩
给定大小为 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(i−1,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(i−1,0)×(n−2)+X(i−1,1)×(n−1)
Y
Y
Y同理
最后就是遍历所有的
X
(
i
,
1
)
和
Y
(
k
−
i
,
1
)
X(i,1)和Y(k-i,1)
X(i,1)和Y(k−i,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); }
给定两个长度为
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(state∣1<<(j−1))=min(f(state∣1<<(j−1)),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]); }
To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。