pku 1170 Shopping Offers 状态DP或者说最短路都可以
看图说话,描述题目- -
有若干类商品,然后每个商品有一定的价格,商店推出捆绑销售优惠,购买指定组合的物品可以有一定的优惠。
给出目标状态,要求花最少的钱买到指定数量的物品。
由于数据量很小,很快想到压缩状态,然后DP求从初始状态到末状态的最小花费。
这种题目我喜欢转化为图,把每个状态看作一个节点,然后求这个图上从起点到终点的最长链。对于某些看似存在后效性的状态DP中,可以采用SPFA算法,消除后效性。
贴代码
1
# include
<
cstdio
>
2 # include < cstring >
3 using namespace std;
4 int dp[ 100000 ];
5 # define encode(n1,n2,n3,n4,n5) (((((((((n1) << 3 ) | (n2)) << 3 ) | (n3)) << 3 ) | (n4)) << 3 ) | (n5))
6 # define getn1(sta) ((sta) >> 12 )
7 # define getn2(sta) (((sta) >> 9 ) & 7 )
8 # define getn3(sta) (((sta) >> 6 ) & 7 )
9 # define getn4(sta) (((sta) >> 3 ) & 7 )
10 # define getn5(sta) ((sta) & 7 )
11 # define min(a,b) ((a) < (b) ? (a):(b))
12 int pri[ 200 ][ 2 ],count = 0 ;
13 int solve( int pos)
14 {
15 if (dp[pos] !=- 1 ) return dp[pos];
16 else if (pos == 0 ) return 0 ;
17 else
18 {
19 // int show1=getn1(pos),show2=getn2(pos),show3=getn3(pos),show4=getn4(pos),show5=getn5(pos);
20 int minnum = 0xfffffff ;
21 for ( int i = count - 1 ;i >= 0 ;i -- )
22 if (getn1(pos) - getn1(pri[i][ 0 ]) >= 0 &&
23 getn2(pos) - getn2(pri[i][ 0 ]) >= 0 &&
24 getn3(pos) - getn3(pri[i][ 0 ]) >= 0 &&
25 getn4(pos) - getn4(pri[i][ 0 ]) >= 0 &&
26 getn5(pos) - getn5(pri[i][ 0 ]) >= 0 &&
27 solve(encode(getn1(pos) - getn1(pri[i][ 0 ]),getn2(pos) - getn2(pri[i][ 0 ]),getn3(pos) - getn3(pri[i][ 0 ]),getn4(pos) - getn4(pri[i][ 0 ]),getn5(pos) - getn5(pri[i][ 0 ]))) + pri[i][ 1 ] < minnum)
28 minnum = solve(encode(getn1(pos) - getn1(pri[i][ 0 ]),getn2(pos) - getn2(pri[i][ 0 ]),getn3(pos) - getn3(pri[i][ 0 ]),getn4(pos) - getn4(pri[i][ 0 ]),getn5(pos) - getn5(pri[i][ 0 ]))) + pri[i][ 1 ];
29 dp[pos] = minnum;
30 return minnum;
31 }
32 }
33 int main()
34 {
35 // FILE *in1=fopen("input.txt","r"),*in2=fopen("offer.txt","r");
36 // FILE *out=fopen("output.txt","w");
37 int num,co = 1 ;
38 int map[ 1000 ];
39 memset(map, - 1 , sizeof (map));
40 memset(dp, - 1 , sizeof (dp));
41 scanf( " %d " , & num);
42 int ori = 0 ;
43 while (num -- )
44 {
45 int c,k,p;
46 scanf( " %d%d%d " , & c, & k, & p);
47 if (map[c] ==- 1 ) map[c] = co ++ ;
48 c = map[c];
49 switch (c)
50 {
51 case 1 :
52 pri[count][ 0 ] = encode( 1 , 0 , 0 , 0 , 0 );
53 ori |= encode(k, 0 , 0 , 0 , 0 );
54 break ;
55 case 2 :
56 pri[count][ 0 ] = encode( 0 , 1 , 0 , 0 , 0 );
57 ori |= encode( 0 ,k, 0 , 0 , 0 );
58 break ;
59 case 3 :
60 pri[count][ 0 ] = encode( 0 , 0 , 1 , 0 , 0 );
61 ori |= encode( 0 , 0 ,k, 0 , 0 );
62 break ;
63 case 4 :
64 pri[count][ 0 ] = encode( 0 , 0 , 0 , 1 , 0 );
65 ori |= encode( 0 , 0 , 0 ,k, 0 );
66 break ;
67 case 5 :
68 pri[count][ 0 ] = encode( 0 , 0 , 0 , 0 , 1 );
69 ori |= encode( 0 , 0 , 0 , 0 ,k);
70 break ;
71 };
72 pri[count ++ ][ 1 ] = p;
73 }
74 scanf( " %d " , & num);
75 while (num -- )
76 {
77 int n;
78 pri[count][ 0 ] = 0 ;
79 bool flag = true ;
80 scanf( " %d " , & n);
81 while (n -- )
82 {
83 int c,k;
84 scanf( " %d%d " , & c, & k);
85 if (map[c] ==- 1 )
86 flag = false ;
87 if (flag)
88 {
89 switch (map[c])
90 {
91 case 1 :
92 pri[count][ 0 ] |= encode(k, 0 , 0 , 0 , 0 );
93 break ;
94 case 2 :
95 pri[count][ 0 ] |= encode( 0 ,k, 0 , 0 , 0 );
96 break ;
97 case 3 :
98 pri[count][ 0 ] |= encode( 0 , 0 ,k, 0 , 0 );
99 break ;
100 case 4 :
101 pri[count][ 0 ] |= encode( 0 , 0 , 0 ,k, 0 );
102 break ;
103 case 5 :
104 pri[count][ 0 ] |= encode( 0 , 0 , 0 , 0 ,k);
105 break ;
106 };
107 }
108 }
109 scanf( " %d " , & n);
110 if (flag)
111 pri[count ++ ][ 1 ] = n;
112 }
113 // for(int i=0;i<count;i++)
114 // printf("%d %d %d %d %d\n",getn1(pri[i][0]),getn2(pri[i][0]),getn3(pri[i][0]),getn4(pri[i][0]),getn5(pri[i][0]));
115 // printf("%d %d %d %d %d\n",getn1(ori),getn2(ori),getn3(ori),getn4(ori),getn5(ori));
116 printf( " %d\n " ,solve(ori));
117 // fclose(in1);
118 // fclose(in2);
119 // fclose(out);
120 return 0 ;
121
122 }
123
2 # include < cstring >
3 using namespace std;
4 int dp[ 100000 ];
5 # define encode(n1,n2,n3,n4,n5) (((((((((n1) << 3 ) | (n2)) << 3 ) | (n3)) << 3 ) | (n4)) << 3 ) | (n5))
6 # define getn1(sta) ((sta) >> 12 )
7 # define getn2(sta) (((sta) >> 9 ) & 7 )
8 # define getn3(sta) (((sta) >> 6 ) & 7 )
9 # define getn4(sta) (((sta) >> 3 ) & 7 )
10 # define getn5(sta) ((sta) & 7 )
11 # define min(a,b) ((a) < (b) ? (a):(b))
12 int pri[ 200 ][ 2 ],count = 0 ;
13 int solve( int pos)
14 {
15 if (dp[pos] !=- 1 ) return dp[pos];
16 else if (pos == 0 ) return 0 ;
17 else
18 {
19 // int show1=getn1(pos),show2=getn2(pos),show3=getn3(pos),show4=getn4(pos),show5=getn5(pos);
20 int minnum = 0xfffffff ;
21 for ( int i = count - 1 ;i >= 0 ;i -- )
22 if (getn1(pos) - getn1(pri[i][ 0 ]) >= 0 &&
23 getn2(pos) - getn2(pri[i][ 0 ]) >= 0 &&
24 getn3(pos) - getn3(pri[i][ 0 ]) >= 0 &&
25 getn4(pos) - getn4(pri[i][ 0 ]) >= 0 &&
26 getn5(pos) - getn5(pri[i][ 0 ]) >= 0 &&
27 solve(encode(getn1(pos) - getn1(pri[i][ 0 ]),getn2(pos) - getn2(pri[i][ 0 ]),getn3(pos) - getn3(pri[i][ 0 ]),getn4(pos) - getn4(pri[i][ 0 ]),getn5(pos) - getn5(pri[i][ 0 ]))) + pri[i][ 1 ] < minnum)
28 minnum = solve(encode(getn1(pos) - getn1(pri[i][ 0 ]),getn2(pos) - getn2(pri[i][ 0 ]),getn3(pos) - getn3(pri[i][ 0 ]),getn4(pos) - getn4(pri[i][ 0 ]),getn5(pos) - getn5(pri[i][ 0 ]))) + pri[i][ 1 ];
29 dp[pos] = minnum;
30 return minnum;
31 }
32 }
33 int main()
34 {
35 // FILE *in1=fopen("input.txt","r"),*in2=fopen("offer.txt","r");
36 // FILE *out=fopen("output.txt","w");
37 int num,co = 1 ;
38 int map[ 1000 ];
39 memset(map, - 1 , sizeof (map));
40 memset(dp, - 1 , sizeof (dp));
41 scanf( " %d " , & num);
42 int ori = 0 ;
43 while (num -- )
44 {
45 int c,k,p;
46 scanf( " %d%d%d " , & c, & k, & p);
47 if (map[c] ==- 1 ) map[c] = co ++ ;
48 c = map[c];
49 switch (c)
50 {
51 case 1 :
52 pri[count][ 0 ] = encode( 1 , 0 , 0 , 0 , 0 );
53 ori |= encode(k, 0 , 0 , 0 , 0 );
54 break ;
55 case 2 :
56 pri[count][ 0 ] = encode( 0 , 1 , 0 , 0 , 0 );
57 ori |= encode( 0 ,k, 0 , 0 , 0 );
58 break ;
59 case 3 :
60 pri[count][ 0 ] = encode( 0 , 0 , 1 , 0 , 0 );
61 ori |= encode( 0 , 0 ,k, 0 , 0 );
62 break ;
63 case 4 :
64 pri[count][ 0 ] = encode( 0 , 0 , 0 , 1 , 0 );
65 ori |= encode( 0 , 0 , 0 ,k, 0 );
66 break ;
67 case 5 :
68 pri[count][ 0 ] = encode( 0 , 0 , 0 , 0 , 1 );
69 ori |= encode( 0 , 0 , 0 , 0 ,k);
70 break ;
71 };
72 pri[count ++ ][ 1 ] = p;
73 }
74 scanf( " %d " , & num);
75 while (num -- )
76 {
77 int n;
78 pri[count][ 0 ] = 0 ;
79 bool flag = true ;
80 scanf( " %d " , & n);
81 while (n -- )
82 {
83 int c,k;
84 scanf( " %d%d " , & c, & k);
85 if (map[c] ==- 1 )
86 flag = false ;
87 if (flag)
88 {
89 switch (map[c])
90 {
91 case 1 :
92 pri[count][ 0 ] |= encode(k, 0 , 0 , 0 , 0 );
93 break ;
94 case 2 :
95 pri[count][ 0 ] |= encode( 0 ,k, 0 , 0 , 0 );
96 break ;
97 case 3 :
98 pri[count][ 0 ] |= encode( 0 , 0 ,k, 0 , 0 );
99 break ;
100 case 4 :
101 pri[count][ 0 ] |= encode( 0 , 0 , 0 ,k, 0 );
102 break ;
103 case 5 :
104 pri[count][ 0 ] |= encode( 0 , 0 , 0 , 0 ,k);
105 break ;
106 };
107 }
108 }
109 scanf( " %d " , & n);
110 if (flag)
111 pri[count ++ ][ 1 ] = n;
112 }
113 // for(int i=0;i<count;i++)
114 // printf("%d %d %d %d %d\n",getn1(pri[i][0]),getn2(pri[i][0]),getn3(pri[i][0]),getn4(pri[i][0]),getn5(pri[i][0]));
115 // printf("%d %d %d %d %d\n",getn1(ori),getn2(ori),getn3(ori),getn4(ori),getn5(ori));
116 printf( " %d\n " ,solve(ori));
117 // fclose(in1);
118 // fclose(in2);
119 // fclose(out);
120 return 0 ;
121
122 }
123