uva 10604Chemical Reaction

来源:互联网 发布:黑马java入学测试 编辑:程序博客网 时间:2024/05/19 15:18

原题:
In a chemists lab, there are several types of chemicals in tubes. The chemist wants to mix all these chemicals together, two chemicals at a time. Whenever two chemicals are mixed, some heat is generated and released into the air and the mixed chemical is a known chemical of possibly other type than the original two. The resulting chemical type and the amount of heats emitted can looked up in the chemical mixture table.
这里写图片描述
For example, in the above chemical mixture table, there are three types of chemicals: 1, 2, and 3. If you mix chemicals 1 and 3, they produce +3000 units of heat and turn into chemical 3. Sometimes, the heat generated can be negative. For instance, you can mix 2 and 3 and they turn into chemical 1 and in the meantime, cool down the lab by 500 units of heat. Since the chemist lacks funding to buy necessary equipments to protect himself from the heat generated, it is utmost important to find a way to mix all the chemicals together that produces the least total heat, regardless of the final chemical type. For example, suppose the lab has four tubes containing chemicals of types 1, 2, 2, and 3. If the chemicals are mixed in the parenthesize order of ((12)(23)), it will produce (−10) + (−500) + (3000) = 2490 units of heat. However, if the chemicals are mixed in the (2(1(23))) order, it will produce (−500)+0+(−10) = −510 units of heat, which is also the least total heat possible.

Input
The first line of input file consists of a single number denoting the number of test cases in the file. There is a single line containing a ‘/’ character separating two consecutive test cases. The end of the file is marked with a line containing a ‘.’. For each test case, the first line contains an integer number m (1 ≤ m ≤ 6) denoting the number chemical types. Therefore, the chemicals are indexed from 1 up to at most 6. The next mxm lines give the chemical mixture table entries in the row-major order. Each line contains the new resulting chemical type and the amount of energy emitted. After the table entries is a line with a single number k (2 ≤ k ≤ 10) denoting number of tubes in the lab. The next line then contains k integers in the range of 1 and m, separated by blank spaces, denoting the types of chemicals in those k tubes.

Output
For each test case, output the least total heat possible on a single line.

Sample Input
2
3
1 0
3 -10
3 3000
3 -10
2 0
1 -500
3 3000
1 -500
3 0
4
1 2 2 3
/
3
1 0
3 500
3 -250
3 500
2 0
1 10
3 -250
1 100
3 0
6
1 1 1 2 2 3
.
Sample Output
-510
-740

中文:
给你一堆药品,每两种药品相互混合可以产生另外一种药品,而且还会放出热量。现在给你一个药品生成表,然后给你一堆药品。问你怎样混合以后放出的热量最小?
(注意a药品和b药品混合与b药品和a药品混合产生的结果会不同,参考样例二)

注意此题样例数据的第二个结果是错的!!答案应该是-740

#include<iostream>#include<bitset>#include<algorithm>using namespace std;const int inf = 99999999;int m, k, t;int change[11][11], heat[11][11];int seq[(1 << 10) + 1][7];int tube[11];int dp(int S, int K){    if (seq[S][K]<inf || 0 == S)//找到解以后,或者没有解返回        return seq[S][K];    if(seq[S][K]==inf+1)//没有解        return seq[S][K];    for (int i = 1; i <= ((1 << k) - 1); i++)    {        int tmpa, tmpb;        if ((i&S) == i)        {            for (int x = 1; x <= m; x++)            {                for (int y = 1; y <= m; y++)                {                    if (change[x][y] != K)                        continue;                    tmpa = dp(S^i, x);                    if (tmpa<inf)                    {                        tmpb = dp(i, y);                        if (tmpb<inf)                        {                            seq[S][K] = min(seq[S][K], tmpa + tmpb + heat[x][y]);                        }                    }                }            }        }    }    if(seq[S][K]==inf)//已经搜索过的情况,而且确定没有解,要记得保存状态,否则会再次搜索,超时-_-        seq[S][K]++;//在inf上面+1,表示达不到的状态,而且已经搜索过了    return seq[S][K];}int main(){    ios::sync_with_stdio(false);    cin >> t;    while (t--)    {        cin >> m;        for (int i = 1; i <= m; i++)        {            for (int j = 1; j <= m; j++)            {                int c, h;                cin >> c >> h;                change[i][j] = c;                heat[i][j] = h;            }        }        cin >> k;        for (int i = 1; i <= k; i++)            cin >> tube[i];        for (int i = 0; i<(1 << 10) + 1; i++)        {            for (int j = 0; j<7; j++)                seq[i][j] = inf;        }        for (int i = 1, j = 1; i <= (1 << (k - 1)); i = i << 1, j++)            seq[i][tube[j]] = 0;        for (int i = 1; i <= m; i++)        {            dp((1<<k)-1,i);        }        int ans=inf+1;        for (int i = 1; i <= m; i++)        {            ans=min(ans,seq[(1 << k) - 1][i]);        }        cout<<ans<<endl;        string s;        cin>>s;    }    return 0;}

思路:

典型的动态规划题目,由题目中的数据量可以看出,此题适合用状态压缩的方法解决。由于状态数较少,也可以使用开多维数组的方式。

设seq[S][K]为在状态S的情况下生成K这种类型药品时放出的热量,S为状态压缩的值二进制的每一位保存的是参与反应的药品。例如S=0101时,则表示有第1种和第3种药品参与反应(从左向右数)

那么seq[0101][2]表示的就是第1和第3种药品反应生成第二种药品时产生的热量。
change[i][j]表示药品转换表
heat[i][j]表示放出热量
tube[i]表示给你的药品

由于要转移的状态不是连续的,所以要用到记忆化搜索的方法,搜索函数是dp(S,K),字母含义同上

先说个错误的状态转移方程

seq[S][K]=min(seq[S][K],dp(S^i,j)+min(heat[tube[j][x]],heat[tube[x][j]]))

上面的i代表二进制的第几位,也就是二进制形式代表第几个药品,x表示i的二进制是第几个药品。

上面的放出带入这个数据
3
1 0
3 -10
3 3000
3 -10
2 0
1 -500
3 3000
1 -500
3 0
4
1 2 2 3
正确结果是-240
但是按照上面放出计算出的结果是错误的,因为药品的混合的考虑方式不能按照之前混合好的状态加上另外“1”个新药品。因为,混合方式也可能是这样
seq[1001][1] + seq[0110][2] +heat[1][2]
seq[1001][1]表示第1个和第4个混合生成1
seq[0110][2]表示第2和第3混合生成2

所以正确的状态转移方程应该是

seq[S][K]=min(seq[S][K],dp(S^i,x)+dp(i,y)+heat[x][y])x与y的取值范围为mi的取值从上面的每一位取值变成枚举所有小于S的二进制状态
原创粉丝点击