1-1 两个字符串的所有最长公共子序列
求两个字符串的所有最长公共子序列
输入格式
输入长度≤100的两个字符串。
输入样例1:
ABCBDAB
BDCABA
输入样例2:
ABACDEF
PGHIK
输出格式
输出两个字符串的所有最长公共子序列,若最长公共子序列多于1个,则将所有子序列按字典序从小到大排序后输出。
输出样例1:
BCAB
BCBA
BDAB
输出样例2:
NO
最长公共子序列并不是最长相同字符串,公共子序列不需要连续相等,只需要有相同的序列字符就好。
基本原理为进行两次for循环,s1序列的每个字符都要去对比s2中的每个字符。对比的同时会继承s1前面字符的序列数。(如图所示)
当s1的c == s2的c时,会根据左上方的数字加一,因为左上方的数字,表示除自己以外的先前序列的公共子序列数。当数字不相等时,对比上方和左边的数字,继承先前序列的最大数值。故代码如下:
if s1[i]==s2[j],arr[i][j]=arr[i-1][j-1]+1;
else arr[i][j]=max{ arr[i][j-1], arr[i-1][j] };
获得上面的数组后,需要根据数组反推打印出子序列。因为当字符相等时,是从左上方加一得到;当字符不想同时,是从上方或左边继承得到。可以进行判断,当字符相等时,输出字符,同时往左上方移动;当不相等时,对比上方和左边,往更大值移动。当上方和左边都相同时,表面有多条子序列路线,这时就需要递归处理。故代码如下:
if (s1[i - 1] == s2[j - 1])
{
i--;
j--;
str = s1[i] + str;
}
else
{
if (arr[i - 1][j] > arr[i][j - 1])i--;
else if (arr[i - 1][j] < arr[i][j - 1])j--;
else
{
lsc_print(i - 1, j, str);
lsc_print(i, j - 1, str);
return;
}
}
完整代码如下:
#include<iostream>
#include<string>
#include<set>
std::string s1, s2;
int arr[101][101] = { 0 };//动态规划表
std::set <std::string> lsc_s;
int lsc_max(int m, int n)//求出最大公共子序列的长度
{
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (s1[i - 1] == s2[j - 1])arr[i][j] = arr[i - 1][j - 1] + 1;
else arr[i][j] = std::max(arr[i - 1][j], arr[i][j - 1]);
}
}
return arr[m][n];
}
void lsc_print(int i, int j, std::string str)
{
while (i > 0 && j > 0)
{
if (s1[i - 1] == s2[j - 1])
{
i--;
j--;
str = s1[i] + str;
}
else
{
if (arr[i - 1][j] > arr[i][j - 1])i--;
else if (arr[i - 1][j] < arr[i][j - 1])j--;
else
{
lsc_print(i - 1, j, str);
lsc_print(i, j - 1, str);
return;
}
}
}
if (str.length())lsc_s.insert(str);
}
int main()
{
std::cin >> s1 >> s2;
int m = s1.length();
int n = s2.length();
int len = lsc_max(m, n);
std::string str;
lsc_print(m, n, str);
auto it = lsc_s.begin();
if (lsc_s.empty())
{
std::cout << "NO" << std::endl;
return 0;
}
while (it != lsc_s.end())
{
std::cout << *it << std::endl;
it++;
}
return 0;
}
1-2KK的传言
股票经纪人KK想要在一群人(n个人的编号为0~n-1)中散布一个传言,传言只通过认识的人进行传递,在两个认识的人之间传递消息所需要的时间各有不同,在一群人中,KK想要找出以哪个人为起点开始传递这个传言,可以在耗时最短的情况下认所有人都收到消息。
请编写程序帮KK解决这个问题。
### 输入格式 先输入n m,其中n是人数,m是可传递消息的人群关系数量,以下m行分别输入a b t ,表示 a 向 b 可传递消息,消耗的时间为t。 当输入0时,表示输入结束。
输入样例1:
4 4
0 1 2
0 2 5
0 3 1
2 3 3
6 5
0 1 2
0 2 5
0 3 1
2 3 3
4 5 2
0
输出格式
输出从第几个人开始传递传言耗时最短。如果无解,就输出-1。
输出样例1:
3
-1
先用floyd算法,求解出任意两个点中的最短距离。其算法步骤为:三次for循环,前两层是循环遍历二维数组所有的点,第三层是对任意 **[i][j]** 把每个其他的点**K**对比一次,看看是从i到j的距离短,还是i到k到j的距离更短,取更短值赋值到数组中。代码如下:
//floyd algorithm
void floyd(int n)
{
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < n; k++)
{
if (j == k) continue;
dp[j][k] = std::min(dp[j][k], dp[j][i] + dp[i][k]);
}
}
再计算每个点到所有点的最小值,就每一行的数值相加即可,其中如果有一条边的值没被改变过,可以视为这个点是孤立的无法到达,也就不符合正确答案,视为失败。
对遍历的所有点的距离总和,找出个最小的点,这个点就是我们所找的答案。代码如下:
int get_res(int n)
{
//Calculate the minimum value from each point to other points
std::vector<int>res_sum;
for (int i = 0; i < n; i++)
{
int sum_n = 0;
for (int j = 0; j < n; j++)
{
if (i == j) continue;
sum_n += dp[i][j];
if (dp[i][j] == MAX_INT)
return -1;
}
res_sum.push_back(sum_n);
}
//find the node which less sum path
int min_sum = res_sum[0];
int min_index = 0;
for (int i = 1; i < n; i++)
{
if (min_sum >= res_sum[i])
{
min_sum = res_sum[i];
min_index = i;
}
}
return min_index;
}
完整代码如下:
#include<iostream>
#include<math.h>
#include<vector>
#include <cmath>
#define MAX_INT 9999
#define MIN_INT -9999
std::vector<std::vector<int>>dp = { 101, std::vector<int>(101, MAX_INT) }; //Minimum Path Matrix
//floyd algorithm
void floyd(int n)
{
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < n; k++)
{
if (j == k) continue;
dp[j][k] = std::min(dp[j][k], dp[j][i] + dp[i][k]);
}
}
int get_res(int n)
{
//Calculate the minimum value from each point to other points
std::vector<int>res_sum;
for (int i = 0; i < n; i++)
{
int sum_n = 0;
for (int j = 0; j < n; j++)
{
if (i == j) continue;
sum_n += dp[i][j];
if (dp[i][j] == MAX_INT)
return -1;
}
res_sum.push_back(sum_n);
}
//find the node which less sum path
int min_sum = res_sum[0];
int min_index = 0;
for (int i = 1; i < n; i++)
{
if (min_sum >= res_sum[i])
{
min_sum = res_sum[i];
min_index = i;
}
}
return min_index;
}
int main()
{
int n, m;
while (1)
{
//input n,m
std::cin >> n;
if (n == 0) return 0;
std::cin >> m;
//input a,b,t
while (m--)
{
int a, b, t;
std::cin >> a >> b >> t;
dp[a][b] = t;
dp[b][a] = t;
}
//处理数据获取结果
floyd(n);
int result = get_res(n);
//result
std::cout << result << std::endl;
std::vector<std::vector<int>>dp = { 101,std::vector<int>(101,MAX_INT) };
}
return 0;
}
0-1背包问题
给定一个承重量为C的背包,n个重量分别为w1,w2,w3,wn的物品,物品i放入背包能产生 p_i (>0)的价值(i=1,2,…,n)。
每个物品要么整个放入背包,要么不放。要求找出最大价值的装包方案。
输入格式
输入的第一行包含两个正整数n和C(1≤n≤20),第二行含n个正整数分别表示n个物品的重量,第三行含n个正整数分别表示n个物品放入背包能产生的价值。
输入样例:
4 9
2 3 4 5
3 4 5 7
输出格式
在一行内输出结果,包括最大价值装包方案的价值、具体装包方案,用空格隔开。具体装包方案是n个物品的一个子集,用长度为n的0、1串表示(1表示对应物品被选中,0表示没有被选中)。如果这样的0、1串不唯一,取字典序最大的那个串。
输出样例:
12 1110
具体步骤由两层for循环构成,其二维数组构成如下:
背包可释放容量不断增加,每个物体一个个重量单位都要尝试,在当前容量下,是否应该塞进去。如果是容量塞不进去,那就继承上方,也就是上一个物体的此时的总体价值。如果容量允许塞进去,例如数组[2][3]=4,第二个物体的容量就是3,可以塞进去。但需要注意,我们要对比,放入物体后,价值是否当前最大化。
我们是判断不放入的时候价值f[1][5]=3和放入的时候我们先找到f[1][5-3]的最大价值然后加上现在物品的价值f[i-1][j-w[i]]+v[i]=7,这种情况我们当然选择放入价值更大所以f[2][5]=7。
for(int i=1;i<=n;i++)
{
for(int j=1;j<=bag_weigh;j++)
{
//max(选i物品,不选i物品)
if(j>=weigh[i])
{
dp[i][j]=max(value[i]+dp[i-1][j-weigh[i]],dp[i-1][j]);
}
else dp[i][j]=dp[i-1][j];
}
}
按上述步骤得到数组后:采用回溯法,当前数值如果和上方数值相同,则说明这个物体塞不进来,或是塞不塞都一样。如果和上方数值不一样,说明该物体被放进背包,并且重要,那么回至上方数值,循环反复。
完整代码如下:
#include<iostream>
#include <vector>
using namespace std;
vector<int>weigh;
vector<int>value;
vector<vector<int>>dp(100,vector<int>(100,0));
string choice="000000000000000000000000000";
void dp_opt(int n,int bag_weigh)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=bag_weigh;j++)
{
//max(选i物品,不选i物品)
if(j>=weigh[i])
{
dp[i][j]=max(value[i]+dp[i-1][j-weigh[i]],dp[i-1][j]);
}
else dp[i][j]=dp[i-1][j];
}
}
return;
}
void get_choice(int i,int j)
{
while(i>=1)
{
if(dp[i][j]==dp[i-1][j])
//第i个物品不重要
choice[i]='0';
else
//第i个物品很重要
{
choice[i]='1';
j-=weigh[i];
}
i--;
}
return;
}
int main()
{
//输入数据物品数量、背包最大承重
int n,bag_weigh;
cin>>n>>bag_weigh;
//因为dp数组的第0行和第0列是没有数据的,为0,所以这里统一
weigh.push_back(0);
value.push_back(0);
//输入物品重量以及价值
for(int i=0;i<n;i++)
{
int w;
cin>>w;
weigh.push_back(w);
}
for(int i=0;i<n;i++)
{
int v;
cin>>v;
value.push_back(v);
}
//获得最大收益
dp_opt(n,bag_weigh);
//获取选择
get_choice(n,bag_weigh);
//输出结果
cout<<dp[n][bag_weigh]<<" ";
for(int i=1;i<=n;i++)
cout<<choice[i];
return 0;
}