TopCode SRM 546: StrllRec_字符字典序剪枝

Problem Statement

 For a given string S of length n an inversion is a pair of integers (i, j) such that 0 <= i < j <= n-1 and S[i] > S[j]. (That is, the character at 0-based index i is greater than the character at 0-based index j.) For example, the string "abcab" has 3 inversions: (1, 3), (2, 3), and (2, 4).

Given are ints n and minInv, and a string minStr. We will consider all strings that are permutations of the first n lowercase English letters. That is, these strings have length n and contain each of the first n letters exactly once. Out of these strings, return the lexicographically smallest string R with the following two properties:
  • The number of inversions in R is at least minInv.
  • The string R is not lexicographically smaller than minStr.
If there is no such string, return an empty string instead.

Definition

 
Class:StrIIRec
Method:recovstr
Parameters:int, int, string
Returns:string
Method signature:string recovstr(int n, int minInv, string minStr)
(be sure your method is public)
 
 

Notes

-A string A is lexicographically smaller than a string B if A is a prefix of B or A contains a smaller character at the first position where the strings differ.

Constraints

-n will be between 1 and 20, inclusive.
-minInv will be between 0 and n*(n-1)/2, inclusive.
-minStr will contain between 1 and n characters, inclusive.
-Each character in minStr will be one of the first n lowercase Latin letters.
-All characters in minStr will be unique.

Examples

0) 
 
2
1
"ab"
Returns: "ba"
You must find the lexicographically smallest string that has at least 1 inversion and is not lexicographically smaller than "ab".
1) 
 
9
1
"efcdgab"
Returns: "efcdgabhi"
 
2) 
 
11
55
"debgikjfc"
Returns: "kjihgfedcba"
"kjihgfedcba" is the only string that has at least 55 inversions.
3) 
 
15
0
"e"
Returns: "eabcdfghijklmno"
 
4) 
 
9
20
"fcdebiha"
Returns: "fcdehigba"
 

This problem statement is the exclusive and proprietary property of TopCoder, Inc. Any unauthorized use or reproduction of this information without the prior written consent of TopCoder, Inc. is strictly prohibited. (c)2003, TopCoder, Inc. All rights reserved.     


//题意:给你两个数n,minInv和一个字符串minStr。求一个长度为n的字符串str,使它满足str>=minStr,且这个字符串的逆序数>=minInv。如果有多个这样的字符串输出字典序最小的!

//分析:dfs,题目字符长度最大可达到20,暴力dfs复杂度为n!,肯定会超时! 忙了半天用了三个剪枝终于A了。

剪枝1:对于题目给出的字符串minStr,dfs得到的当前字符串str  (假设已经长度为p) 需满足 str>=minStr.substr(0,p),即str必须大于minStr的前p个字符r, 否则继续dfs得到的str肯定不满足。

剪枝2:dfs过程中得到str不能每次重新计算它的逆序数,需用变量保存。由于dfs过程中一个个的更新str的字符,所以对于每次dfs加入一个字符 C(表明前面的字符已经确定了) 到str中,计算出n个字符中没有被用过的且比C小的字符的个数,因为没有被用过一定会加在当前字符的后面而又比C小,则为当前更新一个字符str所增加的逆序数val。每次dfs累加。

剪枝3(最重要):在剪枝2的基础上,假设当前一次dfs得到一部分str  (个数为p) 的字符,按剪枝2的分析已经可以确定后面的字符不管怎么更新对于前面已经确定的字符来说一定存在逆序数值为val。 而剩余n-p个字符的逆序数的【最大值】可以为(n-p)*(n-p-1)/2 。如果val+(n-p)*(n-p-1)/2 <minInv,则当前dfs肯定不满足。


#line 4 "StrIIRec.cpp"
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;

bool used[30],sign;
char s[30],str[30];
void dfs(int p,int n,int minInv,string minStr,int use) //use记录没更新得到一个字符增加的逆序数,2
{
	int i,j;
	if(p==n&&use>=minInv&&(string)str>=minStr)  // 3
	{
		sign=true;
		return;
	}
	if(use+(n-p)*(n-p-1)/2<minInv)
		return;
	
	for(i=0;i<n;i++)
	{
		if(!used[i])
		{
			used[i]=true;
			str[p]=s[i];
			string s1=minStr.substr(0,p);  // 1
			if(str<s1)
			{
				used[i]=false;
				continue;
			}
			int t=0;
			for(j=0;j<n;j++)  //计算更新当前字符后增加的逆序数
				if(!used[j]&&(j<str[p]-'a'))
					t++;
			dfs(p+1,n,minInv,minStr,use+t);
			if(sign)
				return;
			used[i]=false;
		//	while(i+1<n&&s[i+1]==s[i]) i++;
		}
	}
}
class StrIIRec
{
        public:
        string recovstr(int n, int minInv, string minStr)
        {
        	int i;
    		s[n]='\0';
            	for(i=0;i<n;i++)
            		s[i]='a'+i;  //string 类型的字符串不能直接这样赋值
           	memset(used,false,sizeof(used));
           	str[n]='\0',sign=false;
           	dfs(0,n,minInv,minStr,0);
           	if(!sign)
           		return "";
  			else
  				return (string)str;
        }
        

};


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值