问题描述:
要参加某项比赛,参赛人员需要排成两队(分别定义为A和B,每队人数至少为1且每队人数随意)。每人写一个字母代表自己,字母为a-z或A-Z。例如A对的字母为:c m c,B对字母为:s n m n。
最佳队形为:
1. 每个人前后可有任意个空位,也可以没有空位;
2. 两队在添加空位后长度必需一致。
3. 根据每个人所写的字母,定义A对于B队的距离为相应位置上的字母的距离总和;
4. 两队相应位置的距离定义为:
1)两个非空位的距离定义为它们所对应字母的ASCII码的差的绝对值;
2)空位与其他任意非空位之间的距离为已知的定值K;
3)空位与空位的距离为0.
5. 最佳队形为:在A,B的所有可能的队形中,必定存在等长的队A1,B1,使得A1和B1的距离最小。那么这个队形就是最佳队形。
问题分析:
算法设计中常用的算法思想是枚举,回溯,分治,贪心,动归。显然此题用动归较易解决。动归的关键是定义子问题以及子问题之间的关系,当然,也需要找出最小子问题即能够直接求解出的问题。
我们定义如下子问题形式:
dp(i,j)表示A(1,i)和B(1,j)的最佳队形的长度。那么,问题就是求解出dp(len(A),len(B))。
动归关系式:
dp(i,j) = min { dp(i-1,j)+K, dp(i,j-1)+K, dp(i-1,j-1) + abs(A(i)-B(j)) }
最小子问题:
A(1,i)或B(1,j)与空格之间的距离。
代码实现:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int GetBestQueue(string& A, string& B,int K)
{
int Alength = A.length();
int Blength = B.length();
vector<vector<int> > dp(Alength + 1,vector<int>(Blength+1));
// Initial the array dp
for (int i = 1; i <= Alength; i++)
dp[i][0] = i*K;
for (int i = 1; i <= Blength; i++)
dp[0][i] = i*K;
dp[0][0] = 0;
//Solution
for (int i = 1; i <= B.length(); i++)
{
for (int j = 1; j <= A.length(); j++)
{
if (dp[j - 1][i] < dp[j][i - 1])
dp[j][i] = dp[j - 1][i] + K;
else
dp[j][i] = dp[j][i - 1] + K;
int tem = abs(A[j - 1] - B[i - 1]);
if (dp[j][i] > dp[j - 1][i - 1] + tem)
dp[j][i] = dp[j - 1][i - 1] + tem;
}
}
return dp[Alength][Blength];
}