UVA 10159

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 }

时间: 2024-11-09 04:04:43

UVA 10159的相关文章

UVA 562 Dividing coins --01背包的变形

01背包的变形. 先算出硬币面值的总和,然后此题变成求背包容量为V=sum/2时,能装的最多的硬币,然后将剩余的面值和它相减取一个绝对值就是最小的差值. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 50007 int c[102],d

UVA 10341 Solve It

Problem F Solve It Input: standard input Output: standard output Time Limit: 1 second Memory Limit: 32 MB Solve the equation: p*e-x + q*sin(x) + r*cos(x) + s*tan(x) + t*x2 + u = 0 where 0 <= x <= 1. Input Input consists of multiple test cases and te

UVA 11014 - Make a Crystal(容斥原理)

UVA 11014 - Make a Crystal 题目链接 题意:给定一个NxNxN的正方体,求出最多能选几个整数点.使得随意两点PQ不会使PQO共线. 思路:利用容斥原理,设f(k)为点(x, y, z)三点都为k的倍数的点的个数(要扣掉一个原点O).那么全部点就是f(1),之后要去除掉共线的,就是扣掉f(2), f(3), f(5)..f(n).n为素数.由于这些素数中包括了合数的情况,而且这些点必定与f(1)除去这些点以外的点共线,所以扣掉.可是扣掉后会扣掉一些反复的.比方f(6)在f

[UVa] Palindromes(401)

UVA - 401 Palindromes Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description A regular palindrome is a string of numbers or letters that is the same forward as backward. For example, the string "ABCDED

uva 401.Palindromes

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=342 题目意思:给出一段字符串(大写字母+数字组成).判断是否为回文串 or 镜像串 or 回文镜像串 or 什么都不是.每个字母的镜像表格如下 Character Reverse Character Reverse Character Reverse A A M M Y Y B

[2016-02-19][UVA][129][Krypton Factor]

UVA - 129 Krypton Factor Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu Submit Status Description You have been employed by the organisers of a Super Krypton Factor Contest in which contestants have very high mental and physica

[2016-02-03][UVA][514][Rails]

时间:2016-02-03 22:24:52 星期三 题目编号:UVA 514 题目大意:给定若干的火车(编号1-n),按1-n的顺序进入车站, 给出火车出站的顺序,问是否有可能存在 分析:    FIFO,用栈模拟一遍即可, 方法:    根据输入的顺序,从1-n开始,当前操作的为i 如果i是当前对应的编号,那么直接跳过(进入B) 如果不是,根据当前需求的编号,小于i,就从栈顶弹出一个元素, 看这个元素是否是需求的,是则继续.否则NO 1 2 3 4 5 6 7 8 9 10 11 12 13

uva 11584 Partitioning by Palindromes 线性dp

// uva 11584 Partitioning by Palindromes 线性dp // // 题目意思是将一个字符串划分成尽量少的回文串 // // f[i]表示前i个字符能化成最少的回文串的数目 // // f[i] = min(f[i],f[j-1] + 1(j到i是回文串)) // // 这道题还是挺简单的,继续练 #include <algorithm> #include <bitset> #include <cassert> #include <

uva 10003 Cutting Sticks 简单区间dp

// uva 10003 Cutting Sticks 区间dp // 经典的区间dp // dp(i,j)表示切割小木棍i-j所需要的最小花费 // 则状态转移为dp(i,j) = min{dp(i,k) + dp(k,j) + a[j]-a[i]) // 其中k>i && k<j // a[j] - a[i] 为第一刀切割的代价 // a[0] = 0,a[n+1] = L; // dp数组初始化的时候dp[i][i+1]的值为 0,这表示 // 每一段都已经是切割了的,不