DP专辑
动态规划:利用问题的最优性原理,以自底向上的方式,从子问题的最优解,逐步构造出整个问题的最优解。
0 1 背包,范围内的最大值
i从0到最后递推,每次dp[ i ] = max( dp[ j ] )+fapiao[ i ]) ; 0 =< j < i;
2#include <cstdio>
3#include <cstring>
4using namespace std;
5
6double limit;
7double fapiao[35], dp[35];
8
9int main(){
10// freopen("c:/aaa.txt", "r", stdin);
11 int n, m, i, j, idx;
12 double temp, a, b, c, ans, mmax;
13 char ch;
14 bool flag;
15 while(scanf("%lf %d", &limit, &n), n){
16 memset(fapiao, 0, sizeof(fapiao));
17 memset(dp, 0, sizeof(dp));
18 idx = 0;
19 while(n--){
20 scanf("%d", &m);
21 a = b = c = 0;
22 flag = 0;
23 while(m--){
24 scanf(" %c:%lf", &ch, &temp);
25 if(ch == 'A') a += temp;
26 else if(ch == 'B') b += temp;
27 else if(ch == 'C') c += temp;
28 else {flag = 1; break;}
29 }
30 if(a > 600 || b > 600 || c > 600 || a+b+c > 1000 || flag) continue;
31 fapiao[idx++] = a + b + c;
32 }
33
34 ans = 0;
35 for(i=0; i<idx; ++i){
36 mmax = 0;
37 for(j=0; j<i; ++j){
38 if(dp[j] + fapiao[i] <= limit && mmax < dp[j]){
39 mmax = dp[j];
40 }
41 }
42 dp[i] = mmax + fapiao[i];
43 if(dp[i] > ans) ans = dp[i];
44 }
45
46 printf("%.2lf\n", ans);
47 }
48 return 0;
49}
2.hdoj 2602 Bone Collector
0 1 背包 ,范围内的最大值
状态转移方程 f [ j ] = max ( f [ j ] , f [ j - w [ i ] ] + v [ i ] ); j 表示容量
2#include <cstdio>
3#include <cstring>
4using namespace std;
5
6int n, limit;
7int v[1010], w[1010], f[1010];
8
9int max(int a,int b){
10 if(a > b) return a;
11 else return b;
12}
13
14int work(){
15 int i, j;
16 memset(f, 0, sizeof(f));
17 for(i=0; i<n; ++i){
18 for(j=limit; j>=w[i]; --j){
19 if( f[j-w[i]]+v[i] > f[j] )
20 f[j] = f[j-w[i]]+v[i];
21 }
22 }
23 return f[limit];
24}
25
26int main(){
27// freopen("c:/aaa.txt", "r", stdin);
28 int T, i;
29 scanf("%d", &T);
30 while(T--){
31 scanf("%d %d", &n, &limit);
32 for(i=0; i<n; ++i){
33 scanf("%d", v + i);
34 }
35 for(i=0; i<n; ++i){
36 scanf("%d", w + i);
37 }
38 printf("%d\n", work());
39 }
40 return 0;
41}
42
3.hdoj 1203 i need a offer
01 背包 ,范围内的最小值
题意要求至少有一张offer的最大概率,即1减去 一张也得不到时的最小概率。
状态转移方程:f[ j ] = min(f[ j] , f[ j - m[i]] * g[i] ) ; j表示钱
2#include <cstdio>
3#include <cstring>
4using namespace std;
5
6int n, limit;
7int m[1010];
8double g[1010], f[10010];
9
10double work(){
11 int i, j;
12 memset(f, 0, sizeof(f));
13 for(i=0; i<=limit; ++i){
14 f[i] = 1;
15 }
16 for(i=0; i<n; ++i){
17 for(j=limit; j>=m[i]; --j){
18 if( f[j-m[i]]*g[i] < f[j] )
19 f[j] = f[j-m[i]]*g[i];
20 }
21 }
22 return f[limit];
23}
24
25int main(){
26// freopen("c:/aaa.txt", "r", stdin);
27 int T, i;
28 while(scanf("%d %d", &limit, &n), n+limit){
29 for(i=0; i<n; ++i){
30 scanf("%d %lf", &m[i], &g[i]);
31 g[i] = 1 - g[i];
32 }
33 printf("%.1lf%%\n", 100-100*work());
34 }
35 return 0;
36}
4.hdoj 2955 Robberies
0 1背包 ,范围内的最大值
一开始没注意到这里概率是相乘的,而非相加,所以以概率为背包贡献了一个WA。
状态转移方程:f [ j ] = max ( f [ j ] , f [ j - m [ i ]] * g[i] ) , 这里,j是抢到的钱,f[ j ]为抢到 j 钱的概率,
g [ i ] 为对应的没抓到的概率。
2#include <string.h>
3
4int main(){
5// freopen("c:/aaa.txt", "r", stdin);
6 int T, n, i, j, sum;
7 int m[110];
8 double f[10010], g[110];
9 double temp, limit;
10 scanf("%d", &T);
11 while(T--){
12 scanf("%lf %d", &limit, &n);
13 limit = 1 - limit;
14 memset(f, 0, sizeof(f));
15 f[0] = 1;
16 sum = 0;
17 for(i=0; i<n; ++i){
18 scanf("%d", &m[i]);
19 scanf("%lf", &temp);
20 g[i] = 1 - temp;
21 sum += m[i];
22 }
23
24 for(i=0; i<n; ++i){
25 for(j=sum; j>=m[i]; --j){
26 if(f[j-m[i]]*g[i] > f[j])
27 f[j] = f[j-m[i]]*g[i];
28 }
29 }
30
31 for(i=sum; i>=0; --i){
32 if(f[i] > limit)
33 break;
34 }
35 printf("%d\n",i);
36 }
37 return 0;
38}
5.poj 2063 Investment
完全背包 ,容量内的最大值
状态压缩:由于利息最大为10%,本金最多为1000 000,而且债券都为1000的倍数,
所以,背包最大值为:1000 000 * (1.1 ^40)/ 1000 = 45259 ;
状态转移方程: f [ j + v[ i ] ] = max ( f [ j + v[ i ] ] , f [ j ] + w [ i ] ] ) ;
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7const int maxn = 45300;
8
9int f[maxn], v[15], w[15];
10int n;
11
12int main(){
13// freopen("c:/aaa.txt", "r", stdin);
14 int T, limit, year, m, i, j;
15 int sum, mmax;
16 scanf("%d", &T);
17 while(T--){
18 memset(f, 0, sizeof(f));
19 scanf("%d %d", &limit, &year);
20 scanf("%d", &m);
21 for(i=0; i<m; ++i){
22 scanf("%d %d", &v[i], &w[i]);
23 v[i] /= 1000;
24 }
25
26 for(i=0; i<m; ++i){
27 for(j=0; j<=maxn-v[i]; ++j){
28 if(f[j] + w[i] > f[j+v[i]])
29 f[j+v[i]] = f[j] + w[i];
30 }
31 }
32
33 sum = limit;
34 for(i=0; i<year; ++i){
35 mmax = 0;
36 for(j=0; j<=sum/1000; ++j){
37 if(f[j] > mmax)
38 mmax = f[j];
39 }
40 sum += mmax;
41 }
42
43 printf("%d\n", sum);
44 }
45 return 0;
46}
6.poj 1384 Piggy-Bank
完全背包,求恰好装满的最小值。
f [] 初始值设为-1 , f [ 0] = 0;
状态转移方程:f [ j + v[i] ] = min ( f [ j + v[i] ] , f [ j ] + w[i] );
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7const int maxn = 10010;
8
9int f[maxn], v[510], w[510];
10int n;
11
12int main(){
13// freopen("c:/aaa.txt", "r", stdin);
14 int T, ca, i, j, a, b, limit, m;
15 scanf("%d", &T);
16 for(ca=0; ca<T; ++ca){
17 scanf("%d %d", &a, &b);
18 limit = b - a;
19
20 scanf("%d", &m);
21 for(i=0; i<m; ++i){
22 scanf("%d %d", &w[i], &v[i]);
23 }
24
25 memset(f, -1, sizeof(f));
26 f[0] = 0;
27 for(i=0; i<m; ++i){
28 for(j=0; j<=limit-v[i]; ++j){
29 if(f[j]!=-1){
30 if(f[j+v[i]] == -1)
31 f[j+v[i]] = f[j] + w[i];
32 else if(f[j] + w[i] < f[j+v[i]])
33 f[j+v[i]] = f[j] + w[i];
34 }
35 }
36 }
37
38 if(f[limit] == -1)
39 printf("This is impossible.\n");
40 else
41 printf("The minimum amount of money in the piggy-bank is %d.\n", f[limit]);
42 }
43 return 0;
44}
7.poj 2392 Space Elevator
多重背包 ,
此类题目都是背包的cost 和 weight相同
f [ j ] = f [ i ] + w[i]*( 1 … N)
2#include <cstdio>
3#include <cstring>
4#include <algorithm>
5using namespace std;
6
7struct Node{
8 int h, a, c;
9}bk[410];
10
11int f[40010];
12
13bool cmp(Node x, Node y){
14 return x.a < y.a;
15}
16
17int main(){
18// freopen("c:/aaa.txt", "r", stdin);
19 int n, i, j, k, max, temp;
20 scanf("%d", &n);
21 for(i=0; i<n; ++i){
22 scanf("%d %d %d", &bk[i].h, &bk[i].a, &bk[i].c);
23 }
24 sort(bk, bk+n, cmp);
25
26 memset(f, 0, sizeof(f));
27 f[0] = 1;
28 max = 0;
29
30 for(i=0; i<n; ++i){
31 for(j=max; j>=0; --j){
32 if(f[j]){
33 for(k=1; k<=bk[i].c; ++k){
34 temp = j + bk[i].h * k;
35 if(temp > bk[i].a)
36 break;
37 f[temp] = 1;
38 if(temp > max)
39 max = temp;
40 }
41 }
42 }
43 }
44
45 printf("%d\n", max);
46 return 0;
47}
8.poj 1276 Cash Machine
多重背包,和poj 2392差不多
2#include <cstdio>
3#include <cstring>
4#include <algorithm>
5using namespace std;
6
7struct Node{
8 int w, c;
9}bill[15];
10
11int f[1000002];
12
13bool cmp(Node x, Node y){
14 return x.w < y.w;
15}
16
17int main(){
18// freopen("c:/aaa.txt", "r", stdin);
19 int n, limit, i, j, k, max, temp;
20 while(scanf("%d %d", &limit, &n) != EOF){
21 for(i=0; i<n; ++i){
22 scanf("%d %d", &bill[i].c, &bill[i].w);
23 }
24 sort(bill, bill+n, cmp);
25
26 memset(f, 0, sizeof(f));
27 f[0] = 1;
28 max = 0;
29
30 for(i=0; i<n; ++i){
31 for(j=max; j>=0; --j){
32 if(f[j]){
33 for(k=1; k<=bill[i].c; ++k){
34 temp = j + bill[i].w * k;
35 if(temp > limit)
36 break;
37 f[temp] = 1;
38 if(temp > max)
39 max = temp;
40 }
41 }
42 }
43 }
44 printf("%d\n",max);
45 }
46 return 0;
47}
9.hdoj 2191 珍惜现在,感恩生活
典型多重背包,此时cost != weight.即把多重背包转化成完全背包或者0 1 背包。
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7const int maxn = 110;
8
9struct Node{
10 int c, w, g;
11}ns[maxn];
12
13int f[maxn];
14int n, limit;
15
16void ZeroOnePack (int c, int w){
17 int j;
18 for(j=limit; j>=c; --j){
19 if(f[j-c] + w > f[j])
20 f[j] = f[j-c] + w;
21 }
22}
23
24void CompletePack (int c, int w){
25 int j;
26 for (j=0; j<=limit-c; ++j){
27 if (f[j] + w > f[j+c])
28 f[j+c] = f[j] + w;
29 }
30}
31
32void MultiplePack (int c, int w, int g){
33 int k;
34 if (c * g >= limit)
35 CompletePack (c, w);
36
37 k = 1;
38 while (k < g){
39 ZeroOnePack (c * k, w * k);
40 g -= k;
41 k *= 2;
42 }
43 ZeroOnePack (c * g, w * g);
44}
45
46int main(){
47// freopen("c:/aaa.txt", "r", stdin);
48 int T, i;
49 scanf ("%d", &T);
50 while (T--){
51 scanf ("%d %d", &limit, &n);
52 for(i=0; i<n; ++i){
53 scanf ("%d %d %d", &ns[i].c, &ns[i].w, &ns[i].g);
54 }
55
56 memset (f, 0, sizeof(f));
57 for(i=0; i<n; ++i){
58 MultiplePack (ns[i].c, ns[i].w, ns[i].g);
59 }
60
61 printf("%d\n", f[limit]);
62 }
63 return 0;
64}
10.hdoj 2059 龟兔赛跑
一维数组的更新。把起点和终点,以及中间的n个充电站看做n+2个点,
长度为n+2的数组里保存到达该站点所需时间的最优解。
而f [ i ] 的最优解,便是从0 到 i-1 里面某个站充满电一直开到 i 站的最小值
f [ i ] = min ( f [ j ] + time( j - > i) );
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7int main(){
8// freopen("c:/aaa.txt", "r", stdin);
9 int d[110];
10 int N, C, T, VR, VT1, VT2, L;
11 int i, j, len;
12 double f[110], temp, min;
13
14 while(scanf("%d", &L)!=EOF){
15 scanf("%d %d %d", &N, &C, &T);
16 scanf("%d %d %d", &VR, &VT1, &VT2);
17
18 for(i=1; i<=N; ++i){
19 scanf("%d", &d[i]);
20 }
21 d[0] = 0;
22 d[N+1] = L;
23 f[0] = 0;
24
25 for(i=1; i<=N+1; ++i){
26 min = 0xfffffff;
27 for(j=0; j<i; ++j){
28 temp = f[j];
29 if(j) temp += T;
30 len = d[i] - d[j];
31 if(len > C)
32 temp += (len - C) * 1.0 / VT2 + C * 1.0 / VT1;
33 else
34 temp += len * 1.0 / VT1;
35 if(temp < min)
36 min = temp;
37 }
38 f[i] = min;
39 }
40
41 if(f[N+1] > 1.0*L/VR) puts("Good job,rabbit!");
42 else puts("What a pity rabbit!");
43 }
44 return 0;
45}
11.hdoj 1059 Dividing
这题可以看做cost 等于weight那种题型。是否有总数一半的情况。
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7bool f[60010];
8int num[6];
9
10int main(){
11// freopen("c:/aaa.txt", "r", stdin);
12 int a, i, j, k;
13 int max, sum, ca=0, half, temp;
14 while(1){
15 scanf("%d %d %d %d %d %d", num, num+1, num+2, num+3, num+4, num+5);
16 if(num[0] + num[1] + num[2] + num[3] + num[4] + num[5] == 0) break;
17 sum = num[0]*1 + num[1]*2 + num[2]*3 + num[3]*4 + num[4]*5 + num[5]*6;
18 printf("Collection #%d:\n", ++ca);
19
20 if(sum % 2 == 1){
21 printf("Can't be divided.\n\n");
22 continue;
23 }
24
25 memset(f, 0, sizeof(f));
26 f[0] = 1;
27 half = sum / 2;
28 max = 0;
29
30 for(i=0; i<6; ++i){
31 if(num[i]){
32 for(j=max; j>=0; --j){
33 if(f[j]){
34 for(k=1; k<=num[i]; ++k){
35 temp = k * (i + 1) + j;
36 if(temp > half || f[temp])
37 break;
38 f[temp] = 1;
39 if(temp > max)
40 max = temp;
41 if(f[half]) break;
42 }
43 }
44 if(f[half]) break;
45 }
46 }
47 if(f[half]) break;
48 }
49
50 if(f[half])
51 printf("Can be divided.\n\n");
52 else
53 printf("Can't be divided.\n\n");
54 }
55 return 0;
56}
12.hdoj 1231 最大连续子序列
求最大连续子串
dp[ j ] = max ( dp[j-1] + num[ i] , num[i] ) ;
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7struct Node{
8 int v, idx;
9}dp[10010];
10
11int main(){
12// freopen("c:/aaa.txt", "r", stdin);
13 int n, num[10010], max, v;
14 int i, j;
15 bool flag;
16 while(scanf("%d", &n), n){
17 flag = 0;
18 for(i=0; i<n; ++i){
19 scanf("%d", &num[i]);
20 if(num[i]>=0) flag = 1;
21 }
22
23 if(!flag){
24 printf("0 %d %d\n", num[0], num[n-1]);
25 continue;
26 }
27
28 memset(dp, 0, sizeof(dp));
29 dp[0].v = num[0];
30 dp[0].idx = 0;
31
32 for(i=1; i<n; ++i){
33 if(dp[i-1].v + num[i] > num[i]){
34 dp[i].v = dp[i-1].v + num[i];
35 dp[i].idx = dp[i-1].idx;
36 }else{
37 dp[i].v = num[i];
38 dp[i].idx = i;
39 }
40 }
41
42 max = -1;
43 for(i=0; i<n; ++i){
44 if(dp[i].v > max){
45 max = dp[i].v;
46 v = i;
47 }
48 }
49
50 printf("%d %d %d\n", max, num[dp[v].idx], num[v]);
51 }
52 return 0;
53}
13.hdoj 1003 max sum
最大连续子串,同hdoj 1231
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7struct Node{
8 int v, idx;
9}dp[100010];
10
11int main(){
12// freopen("c:/aaa.txt", "r", stdin);
13 int n, num[100010], max, v, ca=0, T;
14 int i, j;
15 scanf("%d", &T);
16 while(T--){
17 if(ca!=0) printf("\n");
18 printf("Case %d:\n",++ca);
19
20 scanf("%d", &n);
21 for(i=0; i<n; ++i){
22 scanf("%d", &num[i]);
23 }
24
25 dp[0].v = num[0];
26 dp[0].idx = 0;
27
28 for(i=1; i<n; ++i){
29 if(dp[i-1].v + num[i] >= num[i]){
30 dp[i].v = dp[i-1].v + num[i];
31 dp[i].idx = dp[i-1].idx;
32 }else{
33 dp[i].v = num[i];
34 dp[i].idx = i;
35 }
36 }
37
38 max = dp[0].v;
39 v = 0;
40 for(i=1; i<n; ++i){
41 if(dp[i].v > max){
42 max = dp[i].v;
43 v = i;
44 }
45 }
46
47 printf("%d %d %d\n", max, dp[v].idx+1, v+1);
48 }
49 return 0;
50}
14.hdoj 1087 Super Jumping! Jumping! Jumping!
求最大上升子串
2#define MAX 10001
3
4int main()
5{
6 int n,a,num[MAX],i,j;
7 __int64 sum[MAX],big,biggest;
8
9 while(scanf("%d",&n),n)
10 {
11 for(i=1;i<=n;++i)
12 {
13 scanf("%d",&a);
14 num[i]=a;
15 }
16
17 // DP构造最大数数组
18 sum[1]=num[1]; biggest=num[1];
19 for(i=2;i<=n;++i)
20 {
21 big=0;
22 for(j=1;j<i;++j)
23 {
24 if(num[j]<num[i] && sum[j]>big)
25 big=sum[j];
26 }
27 sum[i]=big+num[i];
28 if(sum[i]>biggest) biggest=sum[i];
29 }
30
31 printf("%I64d\n",biggest);
32 }
33 return 0;
34}
15.hdoj 2845 Beans
横着或者竖着求一下,变成一维的,然后再求一次。
状态转移方程:f [ j ] = max( f [ j -1 ] , f [ j - 2] + a[ j] ) ;
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7const int maxn = 200010;
8
9int num[maxn], dp[maxn], s[maxn];
10int n, m;
11
12inline int max(int a, int b){
13 if (a > b)
14 return a;
15 else
16 return b;
17}
18
19void solve(){
20 int i, j, k;
21
22 for(i=0; i<n; ++i){
23 dp[0] = num[i*m];
24 if(m>1) dp[1] = max(num[i*m+1], num[i*m]);
25
26 for(j=2; j<m; ++j){
27 dp[j] = max(dp[j-2] + num[i*m+j], dp[j-1]);
28 }
29 s[i] = dp[m-1];
30 }
31
32 dp[0] = s[0];
33 if(n > 1) dp[1] = max(s[1], s[0]);
34
35 for(i=2; i<n; ++i){
36 dp[i] = max(dp[i-1], dp[i-2]+s[i]);
37 }
38 printf("%d\n", dp[n-1]);
39}
40
41void input(){
42 int i, j;
43
44 for(i=0; i<n; ++i){
45 for(j=0; j<m; ++j){
46 scanf("%d", &num[i*m+j]);
47 }
48 }
49}
50
51int main(){
52// freopen("c:/aaa.txt", "r", stdin);
53 while(scanf("%d %d", &n, &m) !=EOF){
54 input();
55 solve();
56 }
57 return 0;
58}
16.hdoj 1159 Common Subsequence
一个最优结构有三个最优子结构,f [ i ][ j ] = max ( f [ i -1][ j -1] , f[ i ] [ j -1], f[i -1][ j ] );
2#include<string>
3#define MAX 1005
4using namespace std;
5
6int main()
7{
8 string stra,strb;
9 int la,lb,l,i,j;
10 static int r[MAX][MAX];
11 while(cin>>stra>>strb)
12 {
13 la=stra.size();
14 lb=strb.size();
15
16 for(i=0;i<=la;++i)
17 r[i][0]=0;
18 for(j=0;j<=lb;++j)
19 r[0][j]=0;
20
21 for(i=1;i<=la;++i)
22 {
23 for(j=1;j<=lb;++j)
24 {
25 if(stra[i-1]==strb[j-1])
26 r[i][j]=r[i-1][j-1]+1;
27 else
28 r[i][j]=r[i][j-1]>r[i-1][j] ? r[i][j-1] : r[i-1][j];
29 }
30 }
31
32 printf("%d\n",r[la][lb]);
33 }
34 return 0;
35}
17.hdoj 1506 Largest Rectangle in a Histogram
每一块的最大面积 = 这块的高度 * 包含这块的最大长度,每一块的长度分为左边长度和右边长度,且初始值为 1 。
而如果左边的长度大于它,则应加入左边的长度,依次往左边递归。
2#include <cstdio>
3using namespace std;
4
5typedef __int64 lld;
6
7const int maxn = 100010;
8int num[maxn], ll[maxn], rr[maxn];
9int n;
10
11void solve(){
12 int i, j;
13 lld max, temp;
14 for(i=1; i<n; ++i){
15 j = i - 1;
16 while(num[j] >= num[i] && j>=0){
17 ll[i] += ll[j];
18 j -= ll[j];
19 }
20 }
21
22 for(i=n-2; i>=0; --i){
23 j = i + 1;
24 while(num[j] >= num[i] && j<n){
25 rr[i] += rr[j];
26 j += rr[j];
27 }
28 }
29
30 max = (lld)(rr[0] + ll[0] - 1) * num[0];
31 for(i=1; i<n; ++i){
32 temp = (lld)(rr[i] + ll[i] - 1) * num[i];
33 if(temp > max)
34 max = temp;
35 }
36
37 printf("%I64d\n", max);
38}
39
40void init(){
41 int i;
42 for(i=0; i<n; ++i){
43 scanf("%d", &num[i]);
44 }
45 for(i=0; i<n; ++i){
46 ll[i] = rr[i] = 1;
47 }
48}
49
50int main(){
51// freopen("c:/aaa.txt", "r", stdin);
52 while(scanf("%d", &n), n){
53 init();
54 solve();
55 }
56 return 0;
57}
18.hdoj 1505 City Game
和hdoj 1506差不多
2#include <cstdio>
3using namespace std;
4
5const int maxn = 1010;
6int n, m, ans;
7int num[maxn], ll[maxn], rr[maxn];
8
9void solve(){
10 int i, j, temp;
11 for(i=1; i<m; ++i){
12 if(num[i]){
13 j = i - 1;
14 while(num[j] && j >= 0 && num[j] >= num[i]){
15 ll[i] += ll[j];
16 j -= ll[j];
17 }
18 }
19 }
20
21 for(i=m-2; i>=0; --i){
22 if(num[i]){
23 j = i + 1;
24 while(num[j] && j < m && num[j] > num[i]){
25 rr[i] += rr[j];
26 j += rr[j];
27 }
28 }
29 }
30
31 for(i=0; i<m; ++i){
32 temp = num[i] * (ll[i] + rr[i] - 1);
33 if(temp > ans)
34 ans = temp;
35 }
36}
37
38int main(){
39// freopen("c:/aaa.txt", "r", stdin);
40 int T, i, j;
41 char ch[10];
42
43 scanf("%d", &T);
44 while(T--){
45 for(i=0; i<m; ++i){
46 num[i] = 0;
47 }
48
49 scanf("%d %d", &n, &m);
50 ans = 0;
51 for(i=0; i<n; ++i){
52 for(j=0; j<m; ++j){
53 scanf("%s", ch);
54 if(ch[0] == 'F'){
55 num[j] ++;
56 ll[j] = rr[j] = 1;
57 }else {
58 num[j] = 0;
59 ll[j] = rr[j] = 0;
60 }
61 }
62 solve();
63 }
64 printf("%d\n", 3 * ans);
65 }
66 return 0;
67}
19.hdoj 2571 命运
状态转移方程:dp[i][ j ] = max( dp[i-1][ j ] , dp[i][ j-1] , dp[i][ k]) + num[i][ j] k 为 j 的因子
2#include <cstdio>
3using namespace std;
4
5int n, m;
6int num[22][1010], dp[22][1010];
7
8void solve(){
9 int i, j, k, temp;
10
11 dp[1][1] = num[1][1];
12 for(i=2; i<=m; ++i){
13 temp = dp[1][i-1];
14 for(j=1; j<i-1; ++j){
15 if(i%j==0 && dp[1][j] > temp)
16 temp = dp[1][j];
17 }
18 dp[1][i] = temp + num[1][i];
19 }
20
21 for(i=2; i<=n; ++i){
22 dp[i][1] = dp[i-1][1] + num[i][1];
23 for(j=2; j<=m; ++j){
24 temp = dp[i][j-1];
25 for(k=1; k<j-1; ++k){
26 if(j%k==0 && dp[i][k] > temp)
27 temp = dp[i][k];
28 }
29 if(dp[i-1][j] > temp) temp = dp[i-1][j];
30 dp[i][j] = temp + num[i][j];
31 }
32 }
33 printf("%d\n", dp[n][m]);
34}
35
36void input(){
37 int i, j;
38
39 scanf("%d %d", &n, &m);
40 for(i=1; i<=n; ++i){
41 for(j=1; j<=m; ++j){
42 scanf("%d", &num[i][j]);
43 }
44 }
45}
46
47int main(){
48// freopen("c:/aaa.txt", "r", stdin);
49 int T;
50 scanf("%d", &T);
51 while(T--){
52 input();
53 solve();
54 }
55 return 0;
56}
20.hdoj 1069 Monkey and Banana
解题思路可以理解为求最大上升子序列
2#include<algorithm>
3#include<cstdio>
4using namespace std;
5
6struct cub
7{
8 int l;
9 int w;
10 int h;
11 int sumhigh;
12};
13
14bool issuit(cub x,cub y)
15{
16 if(x.l>y.l && x.w>y.w || x.w>y.l && x.l>y.w) return 1;
17 return 0;
18}
19
20bool cmp(cub x,cub y)
21{
22 return x.l*x.w>y.l*y.w;
23}
24
25int main()
26{
27 int maxn,a,b,c,i,j,maxhigh,th=1;
28 cub cubs[100];
29 while(scanf("%d",&maxn)!=EOF)
30 {
31 if(maxn==0) break;
32 for(i=1;i<=maxn*3;++i)
33 {
34 scanf("%d %d %d", &a , &b , &c );
35 cubs[i].w=a ; cubs[i].l=b ; cubs[i].h=c ;
36 ++i;
37 cubs[i].h=a ; cubs[i].w=b ; cubs[i].l=c ;
38 ++i;
39 cubs[i].l=a ; cubs[i].h=b ; cubs[i].w=c ;
40 }
41
42 sort(cubs+1,cubs+maxn*3+1,cmp);
43
44 cubs[1].sumhigh=cubs[1].h ;
45
46 for(i=2;i<=maxn*3;++i)
47 {
48 maxhigh=0;
49 for(j=1;j<i;++j)
50 {
51 if(issuit(cubs[j],cubs[i]) && cubs[j].sumhigh>maxhigh)
52 maxhigh=cubs[j].sumhigh;
53 }
54 cubs[i].sumhigh=maxhigh+cubs[i].h;
55 }
56 maxhigh=0;
57 for(i=1;i<=maxn*3;++i)
58 {
59 if(cubs[i].sumhigh>maxhigh) maxhigh=cubs[i].sumhigh;
60 }
61
62 printf("Case %d: maximum height = %d\n",th++,maxhigh);
63 }
64 return 0;
65}
21.hdoj 1171 Big Event in HDU
可以看做cost 等于weight的多重背包(母函数也可以做)
即背包容量等于总值一半,然后求背包消耗内的最大值。
2#include <cstdio>
3using namespace std;
4
5int c[115], g[115];
6bool f[150000];
7
8int main(){
9 // freopen("c:/aaa.txt", "r", stdin);
10 int n, i, j, k, temp;
11 int limit, sum, big;
12
13 while(scanf("%d", &n)!=EOF){
14 if(n < 0) break;
15 if(n == 0) continue;
16
17 sum = 0;
18 for(i=0; i<n; ++i){
19 scanf("%d %d", &c[i], &g[i]);
20 sum += c[i] * g[i];
21 }
22 limit = sum/2;
23
24 for(i=0; i<=limit; ++i) f[i] = 0;
25 f[0] = 1;
26 big = 0;
27
28 for(i=0; i<n; ++i){
29 for(j=big; j>=0; --j){
30 if(f[j]){
31 for(k=1; k<=g[i]; ++k){
32 temp = j + k * c[i];
33 if(temp > limit || f[temp])
34 break;
35 f[temp] = 1;
36 if(temp > big)
37 big = temp;
38 }
39 }
40 }
41 }
42
43 for(i=limit; i>=0; --i){
44 if(f[i]){
45 printf("%d %d\n", sum-i, i);
46 break;
47 }
48 }
49 }
50 return 0;
51}
22.doj 1176 免费馅饼
数塔,按时间逆序DP。
2#include <cstdio>
3#include <cstring>
4using namespace std;
5
6int dp[100010][13];
7int t, T;
8
9inline int max(int a, int b, int c){
10 int d;
11 d = a > b ? a : b;
12 return c > d ? c : d;
13}
14
15void solve(){
16 int i, j;
17
18 for(i=t-1; i>=0; --i){
19 for(j=1; j<13; ++j){
20 dp[i][j] += max(dp[i+1][j-1], dp[i+1][j], dp[i+1][j+1]);
21 }
22 }
23
24 printf("%d\n",dp[0][6]);
25}
26
27void init(){
28 int tm, pos;
29 memset(dp, 0, sizeof(dp));
30 t = 0;
31 while(T--){
32 scanf("%d %d", &pos, &tm);
33 if(tm > t) t = tm;
34 dp[tm][++pos] ++;
35 }
36}
37
38int main(){
39 //freopen("c:/aaa.txt", "r", stdin);
40 while(scanf("%d", &T), T){
41 init();
42 solve();
43 }
44 return 0;
45}
23.hdoj 2159 FAFE
二维完全背包
状态转移方程:f [ j + r[i] ] [ k + 1 ] = max ( f [ j ] [ k ] + jy[ i ] , f [ j + r[i] ] [ k + 1 ] ) ; j : 表示忍耐度,k表示个数,r[i]即杀第i个怪所需的忍耐度,
jy[i]指第i个怪提供的经验值
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7int f[110][110];
8int jy[110], r[110];
9
10int main(){
11// freopen("c:/aaa.txt", "r", stdin);
12 int v, limitr, limitg, n;
13 int i, j, k;
14 bool flag;
15
16 while(scanf("%d %d %d %d", &v, &limitr, &n, &limitg)!=EOF){
17 for(i=0; i<n; ++i){
18 scanf("%d %d", &jy[i], &r[i]);
19 }
20
21 memset(f, 0, sizeof(f));
22
23 for(i=0; i<n; ++i){
24 for(j=0; j<=limitr-r[i]; ++j){
25 for(k=0; k<limitg; ++k){
26 if(f[j][k] + jy[i] > f[j+r[i]][k+1]){
27 f[j+r[i]][k+1]= f[j][k] + jy[i];
28 }
29 }
30 }
31 }
32
33 if(f[limitr][limitg] < v){
34 printf("-1\n");
35 continue;
36 }
37
38 for(i=0; i<=limitr; ++i){
39 if(f[i][limitg] >= v){
40 printf("%d\n", limitr - i);
41 break;
42 }
43 }
44 }
45 return 0;
46}
24.hdoj 2577 how to type
如果str[i]大写:on[i] = min(on[i-1]+1, down[i-1]+2); down[i] = min(on[i-1]+2, down[i-1]+2);
如果str[i]小写:on[i] = min(on[i-1]+2, down[i-1]+2);down[i] = min(on[i-1]+2, down[i-1]+1);
on[i]表示按下第i个字母后灯是亮的,down反之。
2#include <cstring>
3using namespace std;
4
5bool isup(char ch){
6 if(ch >= 'A' && ch <= 'Z') return 1;
7 else return 0;
8}
9
10inline int min(int a, int b){
11 return a < b ? a : b;
12}
13
14int main(){
15// freopen("c:/aaa.txt", "r", stdin);
16 char str[110];
17 int on[110], down[110], len, i;
18 int T;
19 scanf ("%d", &T);
20 while(T--){
21 scanf("%s", str);
22
23 if(isup(str[0])){
24 on[0] = down[0] = 2;
25 }else{
26 on[0] = 2;
27 down[0] = 1;
28 }
29
30 len = strlen(str);
31 for(i=1; i<len; ++i){
32 if(isup(str[i])){
33 on[i] = min(on[i-1]+1, down[i-1]+2);
34 down[i] = min(on[i-1]+2, down[i-1]+2);
35 }else{
36 on[i] = min(on[i-1]+2, down[i-1]+2);
37 down[i] = min(on[i-1]+2, down[i-1]+1);
38 }
39 }
40 on[len-1]++;
41
42 printf("%d\n", min(on[len-1], down[len-1]) );
43 }
44 return 0;
45}
25.hdoj 2844 coins
不说了,看成cost 等于weight的背包
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7bool f[100010];
8int w[110], g[110];
9int n, limit;
10
11int main(){
12// freopen("c:/aaa.txt", "r", stdin);
13 int i, j, k, big, temp;
14 while(scanf("%d %d", &n, &limit), n+limit){
15 for(i=0; i<n; ++i){
16 scanf("%d", &w[i]);
17 }
18 for(i=0; i<n; ++i){
19 scanf("%d", &g[i]);
20 }
21 for(i=0; i<=limit; ++i) f[i] = 0;
22 f[0] = 1;
23
24 big = 0;
25 for(i=0; i<n; ++i){
26 for(j=big; j>=0; --j){
27 if(f[j]){
28 for(k=1; k<=g[i]; ++k){
29 temp = j + k * w[i];
30 if(temp > limit || f[temp])
31 break;
32 f[temp] = 1;
33 if(temp > big)
34 big = temp;
35 }
36 }
37 }
38 }
39
40 temp = 0;
41 for(i=1; i<=limit; ++i){
42 if(f[i]) ++temp;
43 }
44 printf("%d\n", temp);
45 }
46 return 0;
47}
26.hdoj 2830 Matrix Swapping II
dp第一次排第一,纪念下。
求最大子矩阵,且列的顺序可以移动。用hash保存每个高度的个数,然后从高到底,hash [ i ] += hash[i + 1]; 则,area = i * hash[ i ];
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7int n, m;
8int num[1010], hash[1010];
9
10int main(){
11// freopen("c:/aaa.txt", "r", stdin);
12 int i, j, a, big, ans, temp;
13 char ch;
14 while(scanf("%d %d", &n, &m)!=EOF){
15 for(i=0; i<m; ++i){
16 num[i] = 0;
17 }
18
19 ans = 0;
20 for(i=0; i<n; ++i){
21 memset(hash, 0, sizeof(hash));
22 big = 0;
23 getchar();
24 for(j=0; j<m; ++j){
25 ch = getchar();
26 (ch == '1') ? num[j] ++ : num[j] = 0;
27 hash[num[j]] ++;
28 if(num[j] > big) big = num[j];
29 }
30 temp = big * hash[big];
31 for(j=big-1; j>0; --j){
32 hash[j] += hash[j+1];
33 if(j * hash[j] > temp) temp = j * hash[j];
34 }
35 if(temp > ans) ans = temp;
36 }
37 printf("%d\n", ans);
38 }
39 return 0;
40}
27.hdoj 2870 Largest Submatrix
最终的最大矩阵不是a,就是b,或者是c。分别枚举三种情况就可以了
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7int n, m, ans;
8int num[1010], l[1010], r[1010];
9char mp[1010][1010];
10
11void solve(){
12 int i, j, temp;
13 for(i=0; i<m; ++i) l[i] = r[i] = 1;
14 for(i=1; i<m; ++i){
15 j = i - 1;
16 while(j>=0 && num[j]>=num[i]){
17 l[i] += l[j];
18 j -= l[j];
19 }
20 }
21
22 for(i=m-2; i>=0; --i){
23 j = i + 1;
24 while(j<m && num[j]>=num[i]){
25 r[i] += r[j];
26 j += r[j];
27 }
28 }
29
30 for(i=0; i<m; ++i){
31 temp = num[i] * (r[i] + l[i] - 1);
32 if(temp > ans) ans = temp;
33 }
34}
35
36int main(){
37// freopen("c:/aaa.txt", "r", stdin);
38 int i, j;
39 while(scanf("%d %d", &n, &m)!=EOF){
40 for(i=0; i<n; ++i){
41 getchar();
42 for(j=0; j<m; ++j){
43 mp[i][j] = getchar();
44 }
45 }
46
47 ans = 0;
48
49 for(i=0; i<m; ++i) num[i] = 0;
50 for(i=0; i<n; ++i){
51 for(j=0; j<m; ++j){
52 if(mp[i][j]=='a' || mp[i][j]=='w' || mp[i][j]=='y' || mp[i][j]=='z'){
53 num[j] ++;
54 }else {
55 num[j] = 0;
56 }
57 }
58 solve();
59
60 }
61
62 for(i=0; i<m; ++i) num[i] = 0;
63 for(i=0; i<n; ++i){
64 for(j=0; j<m; ++j){
65 if(mp[i][j]=='b' || mp[i][j]=='w' || mp[i][j]=='x' || mp[i][j]=='z'){
66 num[j] ++;
67 }else {
68 num[j] = 0;
69 }
70 }
71 solve();
72 }
73
74 for(i=0; i<m; ++i) num[i] = 0;
75 for(i=0; i<n; ++i){
76 for(j=0; j<m; ++j){
77 if(mp[i][j]=='c' || mp[i][j]=='x' || mp[i][j]=='y' || mp[i][j]=='z'){
78 num[j] ++;
79 }else {
80 num[j] = 0;
81 }
82 }
83 solve();
84 }
85
86 printf("%d\n", ans);
87 }
88 return 0;
89}
28.hdoj 1789 Doing Homework again
贪心。尽量先做扣分多的,然后尽量往后做。
2#include <cstdio>
3#include <algorithm>
4
5using namespace std;
6
7struct Node{
8 int limit, cost;
9}ns[1010];
10
11int n;
12bool hash[1010];
13
14bool cmp(Node x, Node y){
15 return x.cost > y.cost ;
16}
17
18bool canput(int pos){
19 int i;
20 for(i=ns[pos].limit; i>=1; --i){
21 if(!hash[i]){
22 hash[i] = 1;
23 return 1;
24 }
25 }
26 return 0;
27}
28
29void solve(){
30 int i, ans;
31 ans = 0;
32 sort(ns, ns+n, cmp);
33 memset(hash, 0, sizeof(hash));
34 for(i=0; i<n; ++i){
35 if(!canput(i)) ans += ns[i].cost;
36 }
37 printf("%d\n", ans);
38}
39
40void input(){
41 int i;
42 scanf("%d", &n);
43 for(i=0; i<n; ++i) scanf("%d", &ns[i].limit);
44 for(i=0; i<n; ++i) scanf("%d", &ns[i].cost);
45}
46
47int main(){
48// freopen("c:/aaa.txt", "r", stdin);
49 int T;
50 scanf("%d", &T);
51 while(T--){
52 input();
53 solve();
54 }
55 return 0;
56}
29.hdoj 1978 How many ways
起初用点加上左上角可达的点的值,超时了。改成点的值加到它可达的点上去,这样显然要省去一些不必要的操作
2#include <cstdio>
3#include <cstring>
4
5using namespace std;
6
7int n, m;
8int num[110][110], dp[110][110];
9
10void solve(){
11 int i, j, k, h;
12 memset(dp, 0, sizeof(dp));
13 dp[0][0] = 1;
14 for(i=0; i<n; ++i){
15 for(j=0; j<m; ++j){
16 for(k=i; k<=i+num[i][j] && k<n; ++k){
17 for(h=j; h<m; ++h){
18 if(k==i && h==j) continue;
19 if(k-i + h-j >num[i][j]) break;
20 dp[k][h] = (dp[k][h] + dp[i][j])%10000;
21 }
22 }
23 }
24 }
25
26 printf("%d\n", dp[n-1][m-1]);
27}
28
29int main(){
30// freopen("c:/aaa.txt", "r", stdin);
31 int T, i, j;
32 scanf("%d", &T);
33 while(T--){
34 scanf("%d %d", &n, &m);
35 for(i=0; i<n; ++i){
36 for(j=0; j<m; ++j){
37 scanf("%d", &num[i][j]);
38 }
39 }
40
41 solve();
42 }
43 return 0;
44}
30.hdoj 1881 毕业bg
先离开的先举行,按离开的时间排序。
状态转移方程:dp[ j ] = max ( dp[ j - bg[i].l ] + bg[i].h , dp[ j ] );
2 #include <cstdio>
3 #include <cstring>
4 #include <algorithm>
5
6 using namespace std;
7
8 struct Node{
9 int h, l, t;
10 }bg[35];
11
12 int limit, n;
13 int dp[1000];
14
15 bool cmp(Node x, Node y){ return x.t < y.t; }
16
17 void init(){
18 int i, j;
19 limit = 0;
20 for(i=0; i<n; ++i){
21 scanf("%d %d %d", &bg[i].h, &bg[i].l, &bg[i].t);
22 if(bg[i].t > limit) limit = bg[i].t;
23 }
24 sort(bg, bg+n, cmp);
25 }
26
27 void solve(){
28 int i, j, ans=0;
29 for(i=0; i<=limit; ++i) dp[i] = 0;
30
31 for(i=0; i<n; ++i){
32 for(j=bg[i].t; j>=bg[i].l; --j){
33 if(dp[j-bg[i].l]+bg[i].h > dp[j]){
34 dp[j] = dp[j-bg[i].l]+bg[i].h;
35 }
36 }
37 }
38
39 for(i=1; i<=limit; ++i){
40 if(dp[i] > ans) ans = dp[i];
41 }
42
43 printf("%d\n", ans);
44 }
45
46 int main(){
47 // freopen("c:/aaa.txt", "r", stdin);
48 while(scanf("%d", &n)!=EOF){
49 if(n < 0) break;
50 init();
51 solve();
52 }
53 return 0;
54 }
31.hdoj 2386 Dart Challenge
也可以看做是cost == weight的背包变形。
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int cntDartBoard, cntDart;
int scores[102];
int f[30000], f2[30000];
int solve()
{
int i, j, k, g, maxNow, maxScore;
memset(f, 0, sizeof(f));
memset(f2, 0, sizeof(f2));
f[0] = 1;
maxNow = 0;
for(g=1; g<=cntDart; ++g){
for(i=0; i<cntDartBoard-1; ++i){
for(j=maxNow; j>=0; --j){
if(f[j]){
for(k=1; k<=3; ++k){
int temp = j + k * scores[i];
if(temp > maxNow) maxNow = temp;
f2[temp] = 1;
}
}
}
}
for(j=maxNow; j>=0; --j){
if(f[j]){
for(k=1; k<=2; ++k){
int temp = j + k * scores[i];
if(temp > maxNow) maxNow = temp;
f2[temp] =1;
}
}
}
for(j=maxNow; j>=0; --j) f[j] = f2[j] || f[j];
}
int sum = 0;
for(i=0; i<=maxNow; ++i)
if(f[i]) ++ sum;
return sum;
}
int main()
{
//reopen("c:/aaa.txt", "r", stdin);
int T, ca, i;
scanf("%d", &T);
for(ca=1; ca<=T; ++ca){
scanf("%d %d", &cntDartBoard, &cntDart);
for(i=0; i<cntDartBoard; ++i) scanf("%d", &scores[i]);
sort(scores, scores+cntDartBoard);
printf("Scenario #%d:\n%d\n\n", ca, solve());
}
return 0;
}
二维完全背包。
#include <iostream> #include <cstring> using namespace std; struct Node { int w, h, v; }ns[12]; int n, w, h; int f[1001][1001]; int solve() { int i, j, k; memset(f, 0, sizeof(f)); for(i=1; i<=h; ++i) { for(j=1; j<=w; ++j) { for(k=1; k<=n; ++k) { if(i >= ns[k].h && j >= ns[k].w) { f[i][j] = max(f[i][j], f[i-ns[k].h][j] + f[ns[k].h][j-ns[k].w] + ns[k].v); f[i][j] = max(f[i][j], f[i][j-ns[k].w] + f[i-ns[k].h][ns[k].w] + ns[k].v); } swap(ns[k].w, ns[k].h); if(i >= ns[k].h && j >= ns[k].w) { f[i][j] = max(f[i][j], f[i-ns[k].h][j] + f[ns[k].h][j-ns[k].w] + ns[k].v); f[i][j] = max(f[i][j], f[i][j-ns[k].w] + f[i-ns[k].h][ns[k].w] + ns[k].v); } } } } return f[h][w]; } int main() { // freopen("c:/aaa.txt", "r", stdin); // freopen("e:/aaa.txt", "w", stdout); int T, i; scanf("%d", &T); while(T--) { scanf("%d %d %d", &n, &h, &w); for(i=1; i<=n; ++i) scanf("%d %d %d\n", &ns[i].h, &ns[i].w, &ns[i].v); printf("%d\n", solve()); } return 0; }
变形01背包,只要排序下就可以了
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; struct Node { int a, b, c; }ns[505]; int n, limit; int dp[5005]; bool cmp(Node n1, Node n2) { return (n1.b - n1.a) < (n2.b - n2.a); } void init() { int i; for(i=0; i<n; ++i) { scanf("%d %d %d", &ns[i].a, &ns[i].b, &ns[i].c); } sort(ns, ns+n, cmp); memset(dp, 0, sizeof(dp)); } int max(int a, int b) { return a > b ? a : b ; } void solve() { int i, j; for(i=0; i<n; ++i) { for(j=limit; j>=ns[i].b; --j) { dp[j] = max(dp[j-ns[i].a] + ns[i].c, dp[j]); // printf("%d ", dp[j]); } // printf("\n"); } int ans = 0; for(i=0; i<=limit; ++i) if(dp[i] > ans) ans = dp[i]; printf("%d\n", ans); } int main() { // freopen("c:/aaa.txt", "r", stdin); while(scanf("%d %d", &n, &limit)!=EOF) { init(); solve(); } return 0; }
34.hdoj 1423 Greatest Common Increasing Subsequence
LCIS--Longest Common Increasing Subsequence
#include <iostream> using namespace std; int len[505], a[505], b[505]; int n, m; void init() { int i, j; scanf( "%d", &n ); for( i=1; i<=n; ++i ) scanf( "%d", &a[i] ); scanf( "%d", &m ); for( i=1; i<=m; ++i ) scanf( "%d", &b[i] ); } void solve() { int i, j, k; memset( len, 0, sizeof(len) ); len[0] = -1; for( i=1; i<=n; ++i ) { k = 0; for( j=1; j<=m; ++j ) { if( b[j] < a[i] && len[j] > len[k] ) k = j; //len[j]表示在j处的最大长度 if( a[i] == b[j] ) len[j] = (len[k] > 0 ? len[k] : 0 ) + 1; } } int maxx = -1; for( i=1; i<=m; ++i ) { if( len[i] > maxx ) maxx = len[i]; } printf( "%d\n", maxx ); } int main() { // freopen( "c:/aaa.txt", "r", stdin ); int ca, T; scanf( "%d", &T ); for( ca=1; ca<=T; ++ca ) { if( ca != 1 ) printf( "\n" ); init(); solve(); } return 0; }
横坐标上有n个点,然后选m个点,使得n个点中每个点离所选的点的最近距离的和的最小值。
dp[i][j] = min(dp[k][j-1] + dis[k+1][i]); j-2<=k <=i-1,点的下标从0开始,i表示点的下标,j表示选的点的个数,dp[i][j]表示0-i中选出j个点的最小值。
#include <iostream> #include <cmath> using namespace std; int n, m, num[205], dis[205][205], dp[202][35]; void input() { for( int i=0; i<n; ++i ) scanf( "%d", &num[i] ); } void getDis() { int i, j, k, mid; memset( dis, 0, sizeof(dis) ); for( i=0; i<n; ++i ) { for( j=i+1; j<n; ++j ) { mid = (i+j) / 2; for( k=i; k<=j; ++k ) { dis[i][j] += abs(num[k] - num[mid]); } } } } void solve() { int i, j, k; memset( dp, 0, sizeof( dp )); for( i=0; i<n; ++i ) dp[i][1] = dis[0][i]; for( i=2; i<=m; ++i ) { for( j=i-1; j<n; ++j ) { dp[j][i] = 9999999; for( k=i-2; k<=j-1; ++k ) { if( dp[k][i-1] + dis[k+1][j] < dp[j][i] ) dp[j][i] = dp[k][i-1] + dis[k+1][j]; } } } printf( "Total distance sum = %d\n\n", dp[n-1][m]); } int main() { // freopen( "c:/aaa.txt", "r", stdin ); int ca = 1; while( scanf("%d %d", &n, &m), n+m ) { printf( "Chain %d\n", ca++ ); input(); getDis(); solve(); } return 0; }
36.hdoj 1158 Employment Planning
DP题,做了两个小时,中间被其他事打扰了几次,1A,效率排在第三,无限兴奋。
一个人一个人地从第一个月到最后一个月DP。每个月有两种情况dp[0][i]和dp[1][i],dp[0][i] 表示在i位置没有人,dp[1][i]表示有人。
(1)当i位置无用人需求时
dp[0][i] = min(dp[1][i-1]+fire, dp[0][i-1]);
dp[1][i] = min(dp[1][i-1]+salary, dp[0][i-1]+hire+salary );
(2)当i位置有用人需求时
dp[0][i] = inf;既然i位置有用人需求,就不可能存在没人的情况。
dp[1][i] = min(dp[0][i-1]+hire+salary, dp[1][i-1]+salary);
#include <iostream> using namespace std; const int inf = 99999999; int M, num[15], hire, fire, salary, dp[2][15]; int minn, ans; int min( int a, int b ) { return a < b ? a : b ; } int solve() { int i, j, minn; bool notFire; minn = 99999999; for( i=0; i<M; ++i ) if( num[i] < minn ) minn = num[i]; ans = minn * ( hire + salary * M ); for( i=0; i<M; ++i ) num[i] -= minn; notFire = true; while( true ) { int s = 0; while( s < M && num[s] == 0 ) ++s; if( s == M ) break; dp[0][s] = inf; dp[1][s] = hire + salary; for( i=s+1; i<M; ++i ) { int tp = (num[i] == 0) ? 0 : 1; if( tp ) { dp[0][i] = inf; dp[1][i] = min( dp[0][i-1] + hire + salary, dp[1][i-1] + salary ); } else { dp[0][i] = min( dp[0][i-1], dp[1][i-1] + fire ); dp[1][i] = min( dp[1][i-1] + salary, dp[0][i-1] + hire + salary ); } } ans += min( dp[0][M-1], dp[1][M-1] ); for( i=0; i<M; ++i ) if( num[i] != 0 ) num[i] --; } return ans; } int main() { // freopen( "c:/aaa.txt", "r", stdin ); int i; while( scanf("%d", &M), M ) { scanf( "%d %d %d", &hire, &salary, &fire); for( i=0; i<M; ++i ) scanf( "%d", &num[i] ); printf( "%d\n", solve() ); } return 0; }
记忆化搜索,本质还是dp的递归过程,所以在搜索前应该把最优解的状态想好。
dp[z][x][y] z:表示此刻h比d多的个数 x:表示已经走了x个h y:表示已经走了y个d
#include <iostream> using namespace std; typedef __int64 lld; lld dp[22][22][22];//记忆化搜索,dp[z][x][y] //z:表示此刻h比d多的个数 //x:表示已经走了x个h //y:表示已经走了y个d lld f( int n, int m, int x, int y ) { if( n-x == 0 || m-y == 0 ) return dp[x-y][n-x][m-y] = 1, 1; //只剩下h或者d,直接返回1 if( dp[x-y][n-x][m-y] != 0 ) return dp[x-y][n-x][m-y]; //如果这个状态搜索过了,直接返回 if( x == y ) return dp[0][n-x][m-y] = f( n, m, x+1, y ), dp[0][n-x][m-y]; //如果之前d和h的个数相同,那么下一步只能走h return dp[x-y][n-x][m-y] = f( n, m, x+1, y ) + f( n, m, x, y+1 ), dp[x-y][n-x][m-y]; //当z>0时,下一步即可走h又可走d } void init() { int i, j; memset( dp, 0, sizeof(dp) ); for(i=1; i<=20; ++i ) { for( j=1; j<=i; ++j ) { dp[0][i][j] = f( i, j, 0, 0 ); } } } int main() { int n, m; init(); while( scanf( "%d %d", &n, &m ) == 2 ) { printf( "%I64d\n", dp[0][n][m] ); } return 0; }
38.hdoj 1400 Mondriaan's Dream
偷看来的,不解释。。。
#include <iostream> #include <cstdio> using namespace std; typedef __int64 lld; lld f[12][2048]; int n, m; void dfs( int i, int j, int jj, int s ) {//j是初始状态,jj是目标状态.s表示列数 if( s == m ) //把i行m列放好 f[i+1][jj] += f[i][j]; //等于I+1行被占去的格子的2进制为JJ应该可以多放f[i][j]的方略 else if( (jj&(1<<s)) == 0 ) { //表示第J列能放1/0 dfs( i, j, jj|(1<<s), s+1 ); //放1 if( s<m-1 && (jj&(1<<(s+1))) == 0 ) dfs( i, j, jj, s+2 ); //放0(横占2格) } else //表示此处只能放0 dfs( i, j, jj&~(1<<s), s+1 ); //(jj & (1 << s)这个位置已经被占 } int main() { int i, j; while( scanf( "%d %d", &n, &m), n ) { memset( f, 0, sizeof(f) ); f[0][0] = 1; for( i=0; i<n; ++i ) { for( j=0; j<(1<<m); ++j ) { if( f[i][j] != 0 ) //剪枝(为0没有必要考虑) dfs(i, j, j, 0); //不占n行也即0~n-1放满的方法数 } } printf( "%I64d\n", f[n][0] ); } return 0; }
.