dp+思维(字符的贡献)

题目1:《字串排序》真题练习
题目大意: 给一个整数 V V V,需要求在冒泡排序次数等于 V V V下的最小字典序字符串。我们定义字典序 a < b aa<b,满足 l e n ( a ) < l e n ( b ) len(a)len(a)<len(b)或者 l e n ( a ) = = l e n ( b ) len(a)==len(b) len(a)==len(b)同时存在位置 p o s pos pos满足 ∀ i < p o s , a [ i ] = = b [ i ] ; a [ p o s ] = = b [ p o s ] \forall ii<pos,a[i]==b[i];a[pos]==b[pos]
解题思路 :经过一系列的基操分析,我们得出了一系列的结论:

  1. 对于长度为 l e n len len的字符串来说,其最大交换次数为 l e n ∗ ( l e n − 1 ) / 2 len*(len-1)/2 len(len1)/2,比如如果长度为 5 5 5,那么此时的字符串应该是 e d b c a edbca edbca
  2. 如果记最右的末尾索引为0,那么在从后往前的位置上最好是放这个位置可以放的最小的,并且从左向右字符串应该是递减的顺序,比如计算出的答案是 a b a a abaa abaa,那么宁可要 b a a baa baa
  3. 相邻字符串应该是紧邻的比如说 a a a b b b
  4. 最右的字符应该从 a a a开始

经过上面这些分析之后,得到字符串应该形如: . . . . c c c b b b a a a ....cccbbbaaa ....cccbbbaaa这样的。
这个时候就可以上动态规划了
d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]为第 i i i个字符(从右向左,从0开始)在前面一个字符为 j j j的情况下能达到总和为 k k k的最小字符。记辅助数组 s [ i ] [ j ] [ k ] s[i][j][k] s[i][j][k]为第 i i i个字符在前一个字符为 j j j,达到总和为 k k k的情况下,前面有 s [ i ] [ j ] [ k ] s[i][j][k] s[i][j][k]个字符小于 i i i
因此我们容易得到 d p dp dp方程为:

   if (j != i)
{
	if (dp[index - 1][j][k].first < dp[index][i][k+index].first)
	{
		dp[index][i][k+index] = { j,k };
		s[index][i][k+index] = index;
		if (k+index == v)
		{
			yh = min(yh, index);
		}
	}
}
    //	dp[index][i][k] = min(dp[index][i][k], dp[index - 1][j][k - index]);
else if(j==i)
{
	if (dp[index - 1][j][k].first < dp[index][i][k+s[index-1][i][k]].first)
	{
		dp[index][i][k + s[index - 1][i][k]] = {i,k };
		s[index][i][k + s[index - 1][i][k]] = s[index - 1][i][k];
		if (k + s[index - 1][i][k] == v)
		{
			yh = min(yh, index);
		}
	}
//	dp[index][i][k] = min(dp[index][i][k], dp[index - 1][i][k / 2]);
}

由于从 k − . . . k-... k... k k k更新过于麻烦,因此改为从 k k k k + s [ ] [ ] k+s[][] k+s[][]更新
复杂度分析: O ( 26 ∗ V V ) O(26*V\sqrt V ) O(26VV ) | V : 1 e 4 V:1e4 V:1e4
全部代码:

// 0328.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include 
#include
#include
#include
using namespace std;
const int length = 1e4 + 5;
pair<int,int>  dp[100][30][length];
int s[100][30][length];
int qianzhui[300];
void init(int tmp,int v)
{
	//int tmp = sqrt(v) + 3;
	for (int i = 0; i <= tmp; i++)
	{
		for (int j = 0; j <= tmp; j++)
		{
			for (int k = 0; k <= v; k++)
			{
				dp[i][j][k] = { 26,0 };
			}
		}
	}
}
int cal(int v)
{
	qianzhui[0] = 0;
	for (int i = 1;; i++)
	{
		qianzhui[i] = qianzhui[i - 1] + i;
		if (qianzhui[i] >= v)
		{
			return i;
		}
	}
}
int main()
{
	int v;
	scanf_s("%d", &v);
	int i = ("baa" < "abaa");
	int yh = INT_MAX;
	int t = 78;
	int tmp = cal(v);
	init(tmp,v);
	dp[0][0][0] = { 0,0 };
	for (int index = 1; index <= tmp; index++)
	{
		if (index == 3)
		{
			int u = 1;
		 }

		for (int i = 0; i <= index; i++)
		{
		//	dp[index][i][0] = { i,0 };
			for (int j = 0; j <= i; j++)
			{
				for (int k = 0; k <= (index - 1)*index / 2; k++)
				{
					if (j != i)
					{
						if (dp[index - 1][j][k].first < dp[index][i][k+index].first)
						{
							dp[index][i][k+index] = { j,k };
							s[index][i][k+index] = index;
							if (k+index == v)
							{
								yh = min(yh, index);
							}
						}
					}
				     //	dp[index][i][k] = min(dp[index][i][k], dp[index - 1][j][k - index]);
					else if(j==i)
					{
						if (dp[index - 1][j][k].first < dp[index][i][k+s[index-1][i][k]].first)
						{
							dp[index][i][k + s[index - 1][i][k]] = {i,k };
							s[index][i][k + s[index - 1][i][k]] = s[index - 1][i][k];
							if (k + s[index - 1][i][k] == v)
							{
								yh = min(yh, index);
							}
						}
					//	dp[index][i][k] = min(dp[index][i][k], dp[index - 1][i][k / 2]);
					}

				}
			}
		}
	}
	for (int i = 0; i < 26; i++)
	{
		if (dp[yh][i][v].first != 26)
		{
			t = i;
			break;
		}
	}
	while (yh != -1)
	{
		printf("%c", t+'a');
		if (yh == 0)
			break;
		int tmp_t=t;
		int tmp_v=v;
		v = dp[yh][tmp_t][tmp_v].second;
		t = dp[yh][tmp_t][tmp_v].first;
		yh--;
	}
}



题目2: 《子串分值》真题练习
题目大意: 求一个字符串的所有子串中只出现一次的字符数量。
题目分析: 处理所有字母(只有小写字母)的出现位置,那么这一个字母可以对 i n d e x [ a l b ] [ i − 1 ] + 1 index[alb][i-1]+1 index[alb][i1]+1 i n d e x [ a l b ] [ i + 1 ] − 1 index[alb][i+1]-1 index[alb][i+1]1的子串做出贡献,为了让这一段包含这个字母,可以构造 b . . . b... b... . . . b ...b ...b . . . b . . . ...b... ...b...来计算所有子串的个数。
代码好像被我删了giao

你可能感兴趣的:(算法题目,算法,图论,c++)