hdu3460 Ancient Printer

唉,又这样贡献了一个WA,忘了把自己的用来测试的输出删了……

一开始还真被这题目吓住了,不过花点心思想想,还是可以做出来的

题意要求的是打印单词的最少操作数

只有三个操作:

输入,删除,还有打印,前缀就不需要重复输入,想到了字典树,这道题目也就好做了

要 求最少的操作数,所以首先要归类,所有拥有相同前缀的单词,放在一块输入,题目还说,最后一次打印不需要删除

理论上是像上面说的,但实际我们在计算最少操作数时,可以这样算,首先假设所有的单词都需要重新打印还有删除,所以总操作数是单词总长度*2

接下来,计算出前缀,可以这样想,只需计算出每一个节点被哪些单词共有,如题目中的样例,“freeradiant”,“freeopen”,f 被俩个节点共有,所以省了一步删除还有一步输入,所以就是(2-1)*2;

最后再找 出最长的一个单词的长度max,很容易理解,最后一行不用删除,那最后一行肯定是越长越好,

根据这三个数值,可以发现,最少的操作=总操作数-总共可以省掉的操作数-多删除掉的最后一行的长度(max)

在代码实现上,额,125ms,不是很快,还是用到了栈还有字典树

更具体的解释,看代码吧

#include<iostream>
#include<stack>
#include<string>
using namespace std;
struct node
{
	node *next[26];
	int v;//记录有多少个单词经过该节点,或者说,有多少单词拥有到该节点为止的前缀
	int lev;//层数,好像用处不是很大
};
node *root;
void insert(char *s)//插入操作
{
	node *p=root;
	for(;*s!='\0';s++)
	{
		int d=*s-'a';
		if(p->next[d]!=NULL)
		{
			p=p->next[d];
			p->v++;
		}
		else 
		{
			p->next[d]=new node();
			p=p->next[d];	
			p->v++;
		}
	}
}
int find()//遍历整棵树
{
	node *p=root,*cur=root,*nex;
	int sum=0;
	stack<node*> nd;
	cur->lev=0;
	nd.push(cur);
	while(!nd.empty())
	{
		cur=nd.top();
		nd.pop();
		if(cur->lev>0)//根节点不计算
		{
		  sum+=(cur->v-1)*2;
		}
		for(int i=0;i<26;i++)
		{
			nex=cur->next[i];
			if(nex!=NULL&&nex->v>1)//单词数大于一,表示有可以省掉的操作,才有必要进行计算,
			{
				nex->lev=cur->lev+1;
				nd.push(nex);//压入栈
			}
		}
	}
	return sum;
}
void del(node *p)//释放空间
{
	for(int i=0;i<26;i++)
	{
		if(p->next[i]!=NULL)
			del(p->next[i]);
	}
	delete(p);
}
int main()
{
	int sum,i,j,n,max,len;
	char str[55];
	while(scanf("%d",&n)!=EOF)
	{
		sum=max=0;
		root=new node();
		for(i=0;i<n;i++)
		{
			scanf("%s",str);
			insert(str);
			len=strlen(str);
			if(len>max)//找出最长的单词
				max=len;
			sum+=len;//计算单词总长度
		}
		j=find();
		sum=sum*2-j-max+n;
		cout<<sum<<endl;
		del(root);
	}
}
原文地址:https://www.cnblogs.com/nanke/p/2046998.html