http://blog.csdn.net/metaphysis/article/details/6926997
先向作者表达一下敬佩吧,十分巧妙地利用了状态压缩。
这道题有点组合数学的味道,当一个格子选后,就把行最大值与格子值相等的行标志位置1.这样,当111111111111即是求的状态了。
这样,可以设一个sum[MAX-1]的数组来对应每一种状态,通过位的或运算的关系来实现状态转移。实在妙极。
原作者代码:
1 // [解题方法] 2 // 根据每个小格所属行的限制来确定该小格内的最大值,然后检测是否满足每行的最大值要求,若不满足,则 3 // 不可能,若满足,求其最大值。对于最小可能值,可以通过动态规划来获得。 4 5 #include <iostream> 6 #include <cstring> 7 #include <sstream> 8 9 using namespace std; 10 11 #define MAXLINES 12 // 行数。 12 #define MAXCELLS 48 // 小格总个数。 13 #define MAXINT 10 14 #define EMPTY (-1) 15 #define MAXTYPES (1 << 12) 16 17 int maxValue[MAXLINES]; // 每行的最大值。 18 int cells[MAXCELLS]; // 小格内数字值。 19 20 // 每个小格属于那些行,行使用题目所给的 A - I 表示。从 0 开始,按从上到下,从左至右的顺序编号。 21 string belongs[MAXCELLS] = { 22 "EL", // 0 23 "EK", "EL", "FL", // 1 - 3 24 "AI", "AI", "AJ", "AEJ", "AEK", "AFK", "AFL", "AGL", "AG", "AH", "AH", // 4 - 14 25 "BI", "BEI", "BEJ", "BFJ", "BFK", "BGK", "BGL", "BHL", "BH", // 15 - 23 26 "CE", "CEI", "CFI", "CFJ", "CGJ", "CGK", "CHK", "CHL", "CL", // 24 - 32 27 "DE", "DE", "DF", "DFI", "DGI", "DGJ", "DHJ", "DHK", "DK", "DL", "DL", // 33 - 43 28 "GI", "HI", "HJ", // 44 - 46 29 "HI" // 47 30 }; 31 32 // 非 EMPTY 元素值表示组成该行的小格编号,从 0 开始,按从上到下,从左至右的顺序编号。 33 int lines[MAXLINES][MAXLINES - 1] = { 34 { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 35 { 15, 16, 17, 18, 19, 20, 21, 22, 23, EMPTY, EMPTY }, 36 { 24, 25, 26, 27, 28, 29, 30, 31, 32, EMPTY, EMPTY }, 37 { 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43 }, 38 { 0, 1, 2, 7, 8, 16, 17, 24, 25, 33, 34 }, 39 { 3, 9, 10, 18, 19, 26, 27, 35, 36, EMPTY, EMPTY }, 40 { 11, 12, 20, 21, 28, 29, 37, 38, 44, EMPTY, EMPTY }, 41 { 13, 14, 22, 23, 30, 31, 39, 40, 45, 46, 47 }, 42 { 4, 5, 15, 16, 25, 26, 36, 37, 44, 45, 47 }, 43 { 6, 7, 17, 18, 27, 28, 38, 39, 46, EMPTY, EMPTY }, 44 { 1, 8, 9, 19, 20, 29, 30, 40, 41, EMPTY, EMPTY }, 45 { 0, 2, 3, 10, 11, 21, 22, 31, 32, 42, 43 } 46 }; 47 48 // 动态规划求最小可能值,这里使用了 tmp 49 // 数组,在原 sum 数组计算的结果先填写在 tmp 数组上,以免持续在 sum 数组上操作引起混乱,同时 50 // 注意最小可能值方案一定是将最大可能值方案中某些小格置 0 而得来的(为什么这样,可以思考一下!)。 51 int dynamic_programming() 52 { 53 int sum[MAXTYPES], tmp[MAXTYPES]; 54 55 // 初始化。 56 memset(sum, EMPTY, sizeof(sum)); 57 58 // 当无任何行匹配时,和最小值为 0. 59 sum[0] = 0; 60 61 for (int i = 0; i < MAXLINES; i++) 62 { 63 // 在副本上操作。 64 memcpy(tmp, sum, sizeof(sum)); 65 for (int j = 0; j < MAXTYPES; j++) 66 if (sum[j] > EMPTY) 67 { 68 for (int k = 0; k < MAXLINES - 1; k++) 69 { 70 // 空小格,该行已处理完毕。 71 if (lines[i][k] == EMPTY) 72 break; 73 74 // 只需处理值为该行最大值的小格。 75 if (cells[lines[i][k]] == maxValue[i]) 76 { 77 int t = j; 78 // cell 表示该小格属于哪些行。 79 string cell = belongs[lines[i][k]]; 80 for (int c = 0; c < cell.length(); c++) 81 // 注意条件!只有当小格的值满足了某行的最大值要求,才计 82 // 入 t。t 的意义是该小格值满足了哪些行的最大值要求。 83 // 使用匹配的行的序号作为移位值来生成一个唯一表示该行的 84 // 整数。 85 if (cells[lines[i][k]] == maxValue[cell[c] - 'A']) 86 t = t | (1 << (cell[c] - 'A')); 87 88 // 更新和的最小值。 89 if (tmp[t] > EMPTY) 90 tmp[t] = min(tmp[t], sum[j] + maxValue[i]); 91 else 92 tmp[t] = sum[j] + maxValue[i]; 93 } 94 } 95 } 96 97 // 获得副本上的结果。 98 memcpy(sum, tmp, sizeof(tmp)); 99 } 100 101 return sum[MAXTYPES - 1]; 102 } 103 104 int main(int ac, char *av[]) 105 { 106 string line; 107 108 while (getline(cin, line)) 109 { 110 // 读入每行的最大值限制。 111 istringstream iss(line); 112 for (int i = 0; i < MAXLINES; i++) 113 iss >> maxValue[i]; 114 115 // 根据限制,获得每个方格的最大值。 116 memset(cells, 0, sizeof(cells)); 117 for (int i = 0; i < MAXCELLS; i++) 118 { 119 int value = MAXINT; 120 for (int j = 0; j < belongs[i].length(); j++) 121 value = min(value, 122 maxValue[belongs[i][j] - 'A']); 123 124 cells[i] = value; 125 } 126 127 // 检查方格的值是否满足最大值要求。 128 bool noSolution = false; 129 for (int i = 0; i < MAXLINES; i++) 130 { 131 int tmp = 0; 132 for (int j = 0; j < MAXLINES - 1; j++) 133 { 134 if (lines[i][j] == EMPTY) 135 break; 136 tmp = max(tmp, cells[lines[i][j]]); 137 } 138 139 if (tmp != maxValue[i]) 140 { 141 noSolution = true; 142 break; 143 } 144 } 145 146 // 输出。 147 if (noSolution) 148 cout << "NO SOLUTION" << endl; 149 else 150 { 151 // 数字和最大可能值。 152 int maxSum = 0; 153 for (int i = 0; i < MAXCELLS; i++) 154 maxSum += cells[i]; 155 156 // 数字和最小可能值。最小可能值的含义是取尽可能少的数字,使得满足所有 157 // 最大值的要求。由于前面的最大可能值方案中已经包含了最小可能值的方案, 158 // 需要将一些小格置为 0 来获得最小可能值,如果小格置 0 的数目越多, 159 // 当然最后和更小,那么就要求一个小格的数字尽可能满足多行的最大值要求, 160 // 这样可以减少非零数字的使用,可以使用动态规划找最小值。 161 int minSum = dynamic_programming(); 162 163 cout << minSum << " " << maxSum << endl; 164 } 165 } 166 167 return 0; 168 }
可怜不知为什么,我的代码竟没能AC。算了,领悟到这样绝妙的思想,已经很满足了。
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <string> 5 #include <sstream> 6 7 #define EMPTY (-1) 8 #define MAXLINES 12 // 行数。 9 #define MAXCELLS 48 // 小格总个数。 10 #define MAXLEN (1<<12) 11 12 using namespace std; 13 14 int lines[MAXLINES][MAXLINES - 1] = { 15 { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 16 { 15, 16, 17, 18, 19, 20, 21, 22, 23, EMPTY, EMPTY }, 17 { 24, 25, 26, 27, 28, 29, 30, 31, 32, EMPTY, EMPTY }, 18 { 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43 }, 19 { 0, 1, 2, 7, 8, 16, 17, 24, 25, 33, 34 }, 20 { 3, 9, 10, 18, 19, 26, 27, 35, 36, EMPTY, EMPTY }, 21 { 11, 12, 20, 21, 28, 29, 37, 38, 44, EMPTY, EMPTY }, 22 { 13, 14, 22, 23, 30, 31, 39, 40, 45, 46, 47 }, 23 { 4, 5, 15, 16, 25, 26, 36, 37, 44, 45, 47 }, 24 { 6, 7, 17, 18, 27, 28, 38, 39, 46, EMPTY, EMPTY }, 25 { 1, 8, 9, 19, 20, 29, 30, 40, 41, EMPTY, EMPTY }, 26 { 0, 2, 3, 10, 11, 21, 22, 31, 32, 42, 43 } 27 }; 28 29 string belongs[MAXCELLS] = { 30 "EL", // 0 31 "EK", "EL", "FL", // 1 - 3 32 "AI", "AI", "AJ", "AEJ", "AEK", "AFK", "AFL", "AGL", "AG", "AH", "AH", // 4 - 14 33 "BI", "BEI", "BEJ", "BFJ", "BFK", "BGK", "BGL", "BHL", "BH", // 15 - 23 34 "CE", "CEI", "CFI", "CFJ", "CGJ", "CGK", "CHK", "CHL", "CL", // 24 - 32 35 "DE", "DE", "DF", "DFI", "DGI", "DGJ", "DHJ", "DHK", "DK", "DL", "DL", // 33 - 43 36 "GI", "HI", "HJ", // 44 - 46 37 "HI" // 47 38 }; 39 40 int maze[50]; 41 int maxtype[15],tes[15]; 42 int sum[MAXLEN],tmp[MAXLEN]; 43 44 int dp(){ 45 memset(sum,-1,sizeof(sum)); 46 sum[0]=0; 47 for(int i=0;i<12;i++){ 48 memcpy(tmp, sum, sizeof(sum)); 49 for(int k=0;k<MAXLEN;k++){ 50 if(sum[k]==-1) 51 continue; 52 for(int p=0;lines[i][p]!=EMPTY&&p<MAXLINES-1;p++){ 53 // if(maze[lines[i][p]]!=maxtype[i]) continue; 54 int t=k; 55 for(int q=0;q<belongs[lines[i][p]].length();q++){ 56 if(maze[lines[i][p]]==maxtype[belongs[lines[i][p]][q]-'A']) 57 t=t|(1<<(belongs[lines[i][p]][q]-'A')); 58 } 59 if(tmp[t]==-1){ 60 tmp[t]=sum[k]+maxtype[i]; 61 } 62 else { 63 tmp[t]=min(tmp[t],sum[k]+maxtype[i]); 64 } 65 } 66 } 67 memcpy(sum, tmp, sizeof(tmp)); 68 // cout<<maxtype[i]<<endl; 69 } 70 return sum[MAXLEN-1]; 71 } 72 73 int main(){ 74 string line; 75 76 while (getline(cin, line)) 77 { 78 // 读入每行的最大值限制。 79 istringstream iss(line); 80 for (int i = 0; i < MAXLINES; i++) 81 iss >> maxtype[i]; 82 memset(tes,-1,sizeof(tes)); 83 for(int i=0;i<50;i++) 84 maze[i]=100; 85 int ans=0; 86 for(int i=0;i<48;i++){ 87 for(int k=0;k<belongs[i].length();k++){ 88 maze[i]=min(maze[i],maxtype[belongs[i][k]-'A']); 89 } 90 for(int k=0;k<belongs[i].length();k++) 91 tes[belongs[i][k]-'A']=max(maze[i],tes[belongs[i][k]-'A']); 92 ans+=maze[i]; 93 } 94 bool flag=true; 95 for(int i=0;i<12;i++) 96 if(tes[i]!=maxtype[i]){ 97 flag=false; 98 break; 99 } 100 if(!flag){ 101 cout<<"NO SOLUTION"<<endl; 102 continue; 103 } 104 int res=dp(); 105 cout<<res<<' '<<ans<<endl; 106 } 107 return 0; 108 }