这篇文章主要介绍线性结构上的动态规划

线性结构上的动态规划

利用线性相关,线性无关,减少dp维度

CH5103
CH5103

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
const int maxn = 50 + 10;
int A[maxn][maxn];
int M, N;
int F[maxn * 2][maxn][maxn];

void init() {
//
Set(A, 0);
}

void initDP() {
//
Set(F, -1);
F[0][1][1] = A[1][1];
}

void dp() {
//
_for(i, 0, M + N - 2) {
for(int x1 = 1; x1 <= M && x1 <= i + 1; x1++) {
int y1 = i + 2 - x1;
for(int x2 = 1; x2 <= M && x2 <= i + 1; x2++) {
int y2 = i + 2 - x2;

// both right, both down
if(x1 == x2) {
F[i + 1][x1][x2] = max(F[i + 1][x1][x2], F[i][x1][x2] + A[x1][y1 + 1]);
F[i + 1][x1 + 1][x2 + 1] = max(F[i + 1][x1 + 1][x2 + 1], F[i][x1][x2] + A[x1 + 1][y1]);
}
else {
F[i + 1][x1][x2] = max(F[i + 1][x1][x2], F[i][x1][x2] + A[x1][y1 + 1] + A[x2][y2 + 1]);
F[i + 1][x1 + 1][x2 + 1] = max(F[i + 1][x1 + 1][x2 + 1], F[i][x1][x2] + A[x1 + 1][y1] + A[x2 + 1][y2]);
}


// x1 down, x2 right
if(x2 == x1 + 1) {
F[i + 1][x1 + 1][x2] = max(F[i + 1][x1 + 1][x2], F[i][x1][x2] + A[x1 + 1][y1]);
}
else F[i + 1][x1 + 1][x2] = max(F[i + 1][x1 + 1][x2], F[i][x1][x2] + A[x1 + 1][y1] + A[x2][y2 + 1]);

// x1 right, x2 down
if(x1 == x2 + 1) {
F[i + 1][x1][x2 + 1] = max(F[i + 1][x1][x2 + 1], F[i][x1][x2] + A[x1][y1 + 1]);
}
else F[i + 1][x1][x2 + 1] = max(F[i + 1][x1][x2 + 1], F[i][x1][x2] + A[x1][y1 + 1] + A[x2 + 1][y2]);
}
}
}
}

int main() {
freopen("input.txt", "r", stdin);
init();
scanf("%d%d", &M, &N);

_rep(i, 1, M) _rep(j, 1, N) scanf("%d", &A[i][j]);

// then initDP() and dp()
initDP();
dp();

cout << F[N + M - 2][M][M] << endl;
}

状态机处理线性dp(状态压缩)

SGU167
SGU167-01
SGU167-02

需要执行方案输出怎么办?
建立一个状态

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Status {
int i, k, l, r, dl, dr;
};
// used to print path
Status pre[maxn][maxn * maxn][maxn][maxn][2][2];

// 在dp的时候
const int last = f[i - 1][k - (r - l + 1)][p][q][1][0];
if(maxv1 < last) {
maxv1 = last;
_pre1 = {i - 1, k - (r - l + 1), p, q, 1, 0};
//debug(_pre1.k);
}
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
const int maxn = 15 + 1;
const int MOD = 16;
const int maxk = 225 + 1;
int N, M, K;
int A[maxn][maxn];

int f[maxn][maxk][maxn][maxn][2][2];
int pre[maxn][maxk][maxn][maxn][2][2];

int code(int l, int r, int dl, int dr) {
return (l << 12) + (r << 8) + (dl << 4) + dr;
}

void decode(const int val, int& l, int& r, int& dr, int& dl) {
l = (val >> 12) % MOD;
r = (val >> 8) % MOD;
dr = (val >> 4) % MOD;
dl = (val) % MOD;
}


void initDP() {
Set(f, 0);
Set(pre, 0);
}

int dp(int& ans, int& fi) {
initDP();

_rep(i, 1, N) _rep(k, 1, K) _rep(l, 1, M) _rep(r, 1, M) {
if(k < (r - l + 1)) continue;

// (spand, spand)
int& _f1 = f[i][k][l][r][1][0];
int& _pre1 = pre[i][k][l][r][1][0];

int maxv1 = 0;
_rep(p, l, r) _rep(q, p, r) {
const int last = f[i - 1][k - (r - l + 1)][p][q][1][0];
if(maxv1 < last) {
maxv1 = last;
//_pre1 = {i - 1, k - (r - l + 1), p, q, 1, 0};
//debug(_pre1.k);
//_pre1 = code(p, q, 1, 0);
_pre1 = (p << 12) + (q << 8) + (1 << 4) + 0;
}
}
_rep(j, l, r) maxv1 += A[i][j];
_f1 = maxv1;

// (spand, shrink)
int& _f2 = f[i][k][l][r][1][1];
int& _pre2 = pre[i][k][l][r][1][1];

int maxv2 = 0;
_rep(p, l, r) _rep(q, r, M) _for(dr, 0, 2) {
const int last = f[i - 1][k - (r - l + 1)][p][q][1][dr];
if(maxv2 < last) {
maxv2 = last;
//_pre2 = code(p, q, 1, dr);
//debug(_pre2.k);
_pre2 = (p << 12) + (q << 8) + (1 << 4) + dr;
}
}
_rep(j, l, r) maxv2 += A[i][j];
_f2 = maxv2;

// (shrink, spand)
int& _f3 = f[i][k][l][r][0][0];
int& _pre3 = pre[i][k][l][r][0][0];

int maxv3 = 0;
_rep(p, 1, l) _rep(q, l, r) _for(dl, 0, 2) {
const int last = f[i - 1][k - (r - l + 1)][p][q][dl][0];
if(maxv3 < last) {
maxv3 = last;
//_pre3 = code(p, q, dl, 0);
//debug(_pre3.k);
_pre3 = (p << 12) + (q << 8) + (dl << 4) + 0;
}
}
_rep(j, l, r) maxv3 += A[i][j];
_f3 = maxv3;

// (shrink, shrink)
int& _f4 = f[i][k][l][r][0][1];
int& _pre4 = pre[i][k][l][r][0][1];

int maxv4 = 0;
_rep(p, 1, l) _rep(q, r, M) _for(dl, 0, 2) _for(dr, 0, 2) {
const int last = f[i - 1][k - (r - l + 1)][p][q][dl][dr];
if(maxv4 < last) {
maxv4 = last;
//_pre4 = code(p, q, dl, dr);
//debug(_pre4.k);
_pre4 = (p << 12) + (q << 8) + (dl << 4) + dr;
}
}
_rep(j, l, r) maxv4 += A[i][j];
_f4 = maxv4;
}

// then get ret;
int ret = 0;
_rep(i, 1, N) _rep(l, 1, M) _rep(r, l, M) _for(dl, 0, 2) _for(dr, 0, 2) {
if(ret < f[i][K][l][r][dl][dr]) {
ret = f[i][K][l][r][dl][dr];
//ans = {i, K, l, r, dl, dr};
// ans = code(l, r, dl, dr);
ans = (l << 12) + (r << 8) + (dl << 4) + dr;
fi = i;
}
}

return ret;
}

void printAns(int ans, int row, int K) {
if(row == 0 || K <= 0) return;
int al, ar, adl, adr;
//decode(ans, al, ar, adl, adr);
al = (ans >> 12) % MOD;
ar = (ans >> 8) % MOD;
adl = (ans >> 4) % MOD;
adr = (ans) % MOD;

_rep(i, al, ar) printf("%d %d\n", row, i);

printAns(pre[row][K][al][ar][adl][adr], row - 1, K - (ar - al + 1));
}

int main() {
freopen("input.txt", "r", stdin);
scanf("%d%d%d", &N, &M, &K);

_rep(i, 1, N) _rep(j, 1, M) scanf("%d", &A[i][j]);

// then dp

int ans, ai;
printf("Oil : %d\n", dp(ans, ai));

printAns(ans, ai, K);
}

线性dp的等效状态

GYM100340A

GYM100340A

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const int maxn = 30 + 5;
const int maxm = 5000 + 5;
const int inf = 0x3f3f3f3f;
int N, M;
int g[maxn], f[maxn][maxm];
int S[maxn];
int prei[maxn][maxm], prej[maxn][maxm];
int ans[maxn];

struct Cld {
int g;
int id;

bool operator< (const Cld& rhs) const {
return g > rhs.g;
}
};
Cld cld[maxn];

void initDP() {
Set(f, inf);
Set(S, 0);

f[0][0] = 0;

_rep(i, 1, N) S[i] = S[i - 1] + cld[i].g;
}

int dp() {
initDP();

_rep(i, 1, N) _rep(j, i, M) {
f[i][j] = f[i][j - i];
//debug(f[i][j]);
prei[i][j] = i;
prej[i][j] = j - i;

_for(k, 0, i) {
int sum = k * (S[i] - S[k]);
if(sum + f[k][j - (i - k)] < f[i][j]) {
f[i][j] = sum + f[k][j - (i - k)];
prei[i][j] = k;
prej[i][j] = j - (i - k);
}
}
}
return f[N][M];
}

void printAns(int i, int j) {
if(i == 0) return;

//debug(prei[i][j]), debug(prej[i][j]);
printAns(prei[i][j], prej[i][j]);
//debug(prei[i][j]), debug(prej[i][j]);

if(i == prei[i][j]) {
_rep(u, 1, i) ans[cld[u].id]++;
}
else {
_rep(u, prei[i][j] + 1, i) ans[cld[u].id] = 1;
}
return;
}

int main() {
freopen("cookies.in","r",stdin);
freopen("cookies.out", "w", stdout);
scanf("%d%d", &N, &M);

_rep(i, 1, N) {
int _g;
scanf("%d", &_g);

cld[i].g = _g;
cld[i].id = i;
}

sort(cld + 1, cld + 1 + N);
// then dp
cout << dp() << endl;
printAns(N, M);

_rep(i, 1, N) {
if(i != 1) printf(" ");
printf("%d", ans[i]);
}
cout << endl;
}

背包问题

0-1背包

0-1-package
CH5201

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const int maxm = 100000 + 10;
const int inf = 0x3f3f3f3f;
int N, M;
const int maxn = 100 + 5;
int A[maxn];
int dp[maxm];

int main() {
freopen("input.txt", "r", stdin);
scanf("%d%d", &N, &M);
_rep(i, 1, N) scanf("%d", &A[i]);

Set(dp, 0);
dp[0] = 1;

_rep(i, 1, N) _forDown(j, M, A[i]) dp[j] += dp[j - A[i]];

cout << dp[M] << endl;
}

0-1背包练习

UVA12563

UVA12563

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
const int maxn = 50 + 5;
int N, T, kase;
const int inf = 0x3f3f3f3f;
int t[maxn];
int f[maxn][maxn * 180 + 678];
int ans = 0;

void initdp() {
ans = 0;
Set(f, -inf);
f[0][T] = 0;
}

void dp() {
// int res;
_rep(i, 1, N) {
_rep(j, 1, T) f[i][j] = f[i - 1][j];

_rep(j, 1, T) {
if(j + t[i] > T) continue;
f[i][j] = max(f[i][j], f[i - 1][j + t[i]] + 1);

ans = max(ans, f[i][j]);
}
}
}

int main() {
freopen("input.txt", "r", stdin);
scanf("%d", &kase);

_rep(cas, 1, kase) {
printf("Case %d:", cas);
scanf("%d%d", &N, &T);
_rep(i, 1, N) scanf("%d", &t[i]);

// then dp
initdp();
dp();
int ret = 0;
for(int j = 1; j <= T; j++) {
if(f[N][j] == ans) {
// ans = f[N][j];
ret = j;
break;
}
}

printf(" %d %d\n", ans + 1, (T - ret) + 678);
}
}

完全背包

full-package
CH5202

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const int maxn = 4000 + 10;
unsigned f[maxn];
int N;
const unsigned MOD = 2147483648u;

void initDP() {
Set(f, 0);
f[0] = 1;
}

int main() {
freopen("input.txt", "r", stdin);
scanf("%d", &N);
initDP();

_rep(i, 1, N) _rep(j, i, N) f[j] += f[j - i];

cout << (f[N] - 1) % MOD << endl;
}

0-1背包多条件(多维度)

POJ1015
POJ1015

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
const int base = 400, maxn = 200 + 10, maxk = 800 + 5;
const int maxm = 20 + 5;
const int inf = 0x3f3f3f3f;


vector<int> ans;
int f[maxm][maxk];
int p[maxn], d[maxn];
int sump, sumd;

struct St {
int i, j, k;
};

St st[maxn][maxm][maxk];

void init() {
ans.clear();
Set(f, -inf);
Set(p, 0);
Set(d, 0);

f[0][0 + base] = 0;
sump = sumd = 0;
}

int N, M;


int dp() {
_rep(i, 1, N) {
_rep(j, 0, M) _rep(k, 0, base * 2) st[i][j][k] = {i - 1, j, k};

for(int j = M; j; j--) {
_rep(k, 0, base * 2) {
if((k - (p[i] - d[i])) < 0 || (k - (p[i] - d[i])) > 800) continue;
if(f[j - 1][k - (p[i] - d[i])] + p[i] + d[i] > f[j][k]) {
f[j][k] = f[j - 1][k - (p[i] - d[i])] + p[i] + d[i];
st[i][j][k] = {i - 1, j - 1, k - (p[i] - d[i])};
}
}
}
}

int ret = 0;
_rep(k, 0, 400) {
if(f[M][400 + k] >= 0 && f[M][400 + k] >= f[M][400 - k]) {
ret = 400 + k;
break;
}

if(f[M][400 - k] >= 0) {
ret = 400 - k;
break;
}
}
return ret;
}

void print(int i, int j, int k) {
if(j == 0) return;
const St pre = st[i][j][k];
print(pre.i, pre.j, pre.k);

if(pre.j != j && f[pre.j][pre.k] >= 0) {
ans.push_back(i);
sump += p[i];
sumd += d[i];
}
}

int main() {
freopen("input.txt", "r", stdin);

int kase = 1;
while ((cin >> N >> M) && (M || N)) {
//debug(M);
init();
printf("Jury #%d\n", kase++);

_rep(i, 1, N) {
cin >> p[i] >> d[i];
}

// then dp;
int ansk = dp();
print(N, M, ansk);


printf("Best jury has value %d for prosecution and value %d for defence:\n", sump, sumd);
//printf("(%d %d)", sump, sumd);
// puts("");
_for(i, 0, ans.size()) printf(" %d", ans[i]);
printf("\n\n");
}
}

线性dp实践(DAG模型))

状态转移方程和图解描述如下

LA2728

LA2728

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
const int maxt = 200 + 5;
const int maxn = 50 + 5;
const int inf = 0x3f3f3f3f;
int T, N;

int f[maxt][maxn];
int t[maxn];
int M1, M2;
int d[maxn], e[maxn];

int hasTrain[maxt][maxn][2];

void initdp() {
Set(f, inf);
f[T][N] = 0;
}

void initTrain() {
Set(hasTrain, 0);

_rep(i, 1, M1) {
int st = d[i];
hasTrain[d[i]][1][0] = 1;

int j = 1;
_rep(k, 1, N - 1) {
st += t[k];
if (st <= T) hasTrain[st][++j][0] = 1;
}
}

_rep(i, 1, M2) {
int st = e[i];
hasTrain[st][N][1] = 1;

int j = N;
_forDown(k, N - 1, 1) {
st += t[k];
if(st <= T) hasTrain[st][--j][1] = 1;
}
}
}

void dp() {
initTrain();
initdp();

_forDown(i, T - 1, 0) _rep(j, 1, N) {
f[i][j] = f[i + 1][j] + 1;

if(hasTrain[i][j][0] && j + 1 <= N && i + t[j] <= T) {
f[i][j] = min(f[i][j], f[i + t[j]][j + 1]);
}

if(hasTrain[i][j][1] && j - 1 >= 1 && i + t[j - 1] <= T) {
f[i][j] = min(f[i][j], f[i + t[j - 1]][j - 1]);
}
}
}

int main() {
freopen("input.txt", "r", stdin);
int kase = 1;

while (scanf("%d", &N) && N) {
scanf("%d", &T);

_rep(i, 1, N - 1) scanf("%d", &t[i]);
scanf("%d", &M1);
_rep(i, 1, M1) scanf("%d", &d[i]);
scanf("%d", &M2);
_rep(i, 1, M2) scanf("%d", &e[i]);


printf("Case Number %d: ", kase++);

// solve the problem:
dp();
if(f[0][1] >= inf) printf("impossible");
else cout << f[0][1];

puts("");
}
}

POJ2241

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
const int maxn = 30 + 5;
int N;

int block[maxn][3];
int f[maxn][3];

void getdim(int* v, int i, int j) {
int id = 0;
_for(k, 0, 3) if(k != j) v[id++] = block[i][k];
}

void initdp() {
Set(f, 0);
}

int dp(int i, int j) {
int& ans = f[i][j];
if(ans > 0) return ans;

ans = 0;
int v1[2], v2[2];
getdim(v1, i, j);

_for(a, 0, N) _for(b, 0, 3) {
getdim(v2, a, b);

if(v1[0] < v2[0] && v1[1] < v2[1]) ans = max(ans, dp(a, b));
}

ans += block[i][j];
return ans;
}


int main() {
freopen("input.txt", "r", stdin);
int kase = 1;
while (scanf("%d", &N) && N) {
_for(i, 0, N) {
_for(j, 0, 3) scanf("%d", &block[i][j]);
sort(block[i], block[i] + 3);
}

// then dp
initdp();
int ret = 0;
_for(i, 0, N) _for(j, 0, 3) ret = max(ret, dp(i, j));
printf("Case %d: maximum height = %d\n", kase++, ret);
}
}

线性dp的递归求解

LA3305

LA3305

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
const int maxn = 1000 + 5;
const int inf = 0x3f3f3f3f;

class Node {
public:
double x, y;
};

Node nodes[maxn];
int N;
double f[maxn][maxn];

double dist(int i, int j) {
return sqrt((nodes[i].x - nodes[j].x) * (nodes[i].x - nodes[j].x) + (nodes[i].y - nodes[j].y) * (nodes[i].y - nodes[j].y));
}

void initdp() {
Set(f, 0.0);
}

double dp(int i, int j) {
double& ans = f[i][j];
if(ans > 0) return ans;

if(i == N - 1) {
//
ans = dist(N - 1, N) + dist(j, N);
}
else {
//
ans = min(dp(i + 1, j) + dist(i, i + 1), dp(i + 1, i) + dist(i + 1, j));
}

return ans;
}

int main() {
freopen("input.txt", "r", stdin);
while (scanf("%d", &N) == 1) {
_rep(i, 1, N) scanf("%lf%lf", &nodes[i].x, &nodes[i].y);

// then dp()
initdp();
double ret = dp(2, 1);

printf("%.2lf\n", ret + dist(2, 1));
}

}

多段图的最短路

UVA116
UVA116

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
const int maxm = 10 + 5, maxn = 100 + 5;
const int inf = 0x3f3f3f3f;

int A[maxm][maxn];
int M, N;

int f[maxm][maxn];
int nxt[maxm][maxn];

int first = 0;
int ans = inf;

void initDP() {
first = 0;
ans = inf;

Set(f, inf);
Set(nxt, 0);
//_rep(i, 1, M) f[i][N] = A[i][N];

// dp _forDown(j = N - 1 down to 1)
}
int dp() {
_forDown(j, N, 1) _rep(i, 1, M) {
if(j == N) {
f[i][j] = A[i][j];
//debug(f[i][j]);
}
else {
//
int ni[3] = {i - 1, i, i + 1};
int nj = j + 1;

if(i == 1) ni[0] = M;
if(i == M) ni[2] = 1;

sort(ni, ni + 3);

_for(k, 0, 3) {
if(f[i][j] > f[ni[k]][nj] + A[i][j]) {
assert(ni[k] != 0 && ni[k] != M + 1);
f[i][j] = f[ni[k]][nj] + A[i][j];
nxt[i][j] = ni[k];
}
}

}
if(j == 1 && ans > f[i][j]) {
assert(f[i][j] != inf);
ans = f[i][j];
first = i;
}

}
return ans;
}

void print(int i, int j) {
if(j > N) return;

if(j != 1) printf(" ");
printf("%d", i);
print(nxt[i][j], j + 1);
}

int main() {
freopen("input.txt", "r", stdin);
while (scanf("%d%d", &M, &N) == 2 && M) {
//debug(N);
_rep(i, 1, M) _rep(j, 1, N) {
scanf("%d", &A[i][j]);
//debug(A[i][j]);
}

// dp
initDP();
int ret = dp();

//print(first, 1);
printf("%d", first);
for(int u = nxt[first][1], j = 2; j <= N; j++, u = nxt[u][j - 1]) printf(" %d", u);

printf("\n%d\n", ret);
}
}