HDU 4534

AC自动机+状态DP。

虽然很明显的AC自动机+状态DP题,但要分析问题上还是欠缺一点了。一直在犹豫枚举每一个字符选或不选的状态会不会超时,以为会达到状态有2^n,但其实根本没有。因为有很多状态是可以重复的,这是由于每一位字符选或不选,都只能转移动自动机的固定位置,所以,状态是有限的。状态压缩是为了确定必须出现的串是否全部出现。于是可以设状态:

dp[i][j][k]为当前处理到第i个字符,处于自动机的j状态,k为必须出现的字符串是否出现的情况。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int root=0;
const int inf=1<<30;
int trie[1650][26];
int fail[1650];
int tag[1650],tot;
int que[1650],head,tail;
int kill[1650],must[1650],score[1650];
int need;
struct Status{
	int act,sc;
	void init(){
		act=inf,sc=-inf;
	}
}dp[2][1650][260];
char str[110];

void init(int cur){
	for(int i=0;i<=tot;i++){
		for(int k=0;k<(1<<(need+1));k++)
		dp[cur][i][k].init();
	}
}

void AddTrie(char *s,int sc){
	int len=strlen(s);
	int i=0,p=root;
	while(len--){
		if(trie[p][s[i]-‘a‘]==-1){
			trie[p][s[i]-‘a‘]=++tot;
			kill[tot]=must[tot]=0;
			memset(trie[tot],-1,sizeof(trie[tot]));
		}
		p=trie[p][s[i]-‘a‘];
		i++;
	}
	if(sc==999){must[p]|=(1<<need);}
	else if(sc==-999) kill[p]=1;
	else {
		score[p]+=sc;
	}
}

void build_ac(){
    head=tail=0;
    que[tail++]=root;
    while(head!=tail){
        int tmp=que[head++];
        int p=-1;
        for(int i=0;i<26;i++){
            if(trie[tmp][i]!=-1){
                if(tmp==root) fail[trie[tmp][i]]=root;
                else{
                    p=fail[tmp];
                    while(p!=-1){
                        if(trie[p][i]!=-1){
                            fail[trie[tmp][i]]=trie[p][i];
                            break;
                        }
                        p=fail[p];
                    }
                    if(p==-1) fail[trie[tmp][i]]=root;
                }
                if(kill[fail[trie[tmp][i]]]) kill[trie[tmp][i]]=1;
                if(must[fail[trie[tmp][i]]]) must[trie[tmp][i]]|=must[fail[trie[tmp][i]]];
                if(score[fail[trie[tmp][i]]]) score[trie[tmp][i]]+=score[fail[trie[tmp][i]]];
                que[tail++]=trie[tmp][i];
            }
            else{
                if(tmp==root) trie[tmp][i]=root;
                else{
                    p=fail[tmp];
                    while(p!=-1){
                        if(trie[p][i]!=-1){
                            trie[tmp][i]=trie[p][i];
                            break;
                        }
                        p=fail[p];
                    }
                    if(p==-1) trie[tmp][i]=root;
                }
            }
        }
    }
}

void Cal(){
	int cur=0,next=1;
	init(cur); init(next);
	int len=strlen(str);
	dp[cur][0][0].act=0; dp[cur][0][0].sc=0;
	for(int i=0;i<len;i++){
//		cout<<i<<endl;
		for(int j=0;j<=tot;j++){
			for(int k=0;k<(1<<(need+1));k++){
				if(dp[cur][j][k].act>=inf) continue;
				int pn=trie[j][str[i]-‘a‘];
				if(kill[pn]){
					if(dp[cur][j][k].act+1<dp[next][j][k].act){
						dp[next][j][k].act=dp[cur][j][k].act+1;
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
					else if(dp[cur][j][k].act+1==dp[next][j][k].act&&dp[cur][j][k].sc>dp[next][j][k].sc){
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
				}
				else{
					int tmpk=k|must[pn];
					if(dp[cur][j][k].act<dp[next][pn][tmpk].act){
						dp[next][pn][tmpk].act=dp[cur][j][k].act;
						dp[next][pn][tmpk].sc=dp[cur][j][k].sc+score[pn];
					}
					else if(dp[cur][j][k].act==dp[next][pn][tmpk].act&&dp[cur][j][k].sc+score[pn]>dp[next][pn][tmpk].sc){
						dp[next][pn][tmpk].sc=dp[cur][j][k].sc+score[pn];
					}
					if(dp[cur][j][k].act+1<dp[next][j][k].act	){
						dp[next][j][k].act=dp[cur][j][k].act+1;
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
					else if(dp[cur][j][k].act+1==dp[next][j][k].act&&dp[cur][j][k].sc>dp[next][j][k].sc){
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
				}
			}
		}

		init(cur);
		cur^=1;
		next^=1;
	}
	int minact=inf,maxsc=-inf;
	for(int i=0;i<=tot;i++){
			if(dp[cur][i][(1<<(need+1))-1].act<minact){
				minact=dp[cur][i][(1<<(need+1))-1].act;
				maxsc=dp[cur][i][(1<<(need+1))-1].sc;
			}
			else if(dp[cur][i][(1<<(need+1))-1].act==minact&&maxsc<dp[cur][i][(1<<(need+1))-1].sc)
			maxsc=dp[cur][i][(1<<(need+1))-1].sc;
	}
	if(minact>=inf)
	puts("Banned");
	else cout<<minact<<" "<<maxsc<<endl;
}

int main(){
	int T,icase=0;
	char s[20];int sc;
	scanf("%d",&T);
	while(T--){
		int n;
		tot=0,need=-1;
		memset(trie[tot],-1,sizeof(trie[tot]));
		memset(score,0,sizeof(score));
		memset(kill,0,sizeof(kill));
		memset(must,0,sizeof(must));
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%s%d",s,&sc);
			if(sc==999) need++;
			AddTrie(s,sc);
		}
//		cout<<"YES"<<endl;
		build_ac();
		scanf("%s",str);
		printf("Case %d: ",++icase);
		Cal();
	}
	return 0;
}

  

时间: 2024-11-02 11:19:54

HDU 4534的相关文章

[AC自动机+状压dp] hdu 4534 郑厂长系列故事——新闻净化

题意:中文的题目,意思就是说有很多串,每个串都有权值,权值为999的串必须出现,-999的串必须不出现.权值在-999~999之间. 然后必须出现的串不超过8个.然后给一个全为小写目标串,问最少需要删除多少个字母才能够保证必须出现的串都出现,次数一样保证权值最大.输出次数和权值. 然后根据样例,那些必须出现的串,其实权值是0. 思路: 很明显一开始建自动机构成trie图,但是需要注意的就是mark和sum的更新.个人是把所有中间的节点的sum全部赋值成了-inf. 接着只有8个必须出现的串,所以

《程序设计中的组合数学》——全错位排列

承接上文,这次以递推的思维,介绍组合学当中一个很经典的问题. 这个问题最开始由瑞士数学家欧拉提出,原始的问题被叫做“装信封问题”,问题的大意就是:有n封信和n封它们各自对应的信封,如果邮递员想要把每封信都放在不属于这封信的信封,那么请问有多少种排法.(这邮递员真无聊)  想必这个问题在中学阶段数学的[排列组合]都有过接触,但是我记忆非常深刻的是,老师讲到这个模型,自己找了一下n = 5的情况就停止了,然后让大家把前面的数字序列背下来.今日故地重游不禁觉得老师教的好坑爹,搞学习还是要亲历亲为自主探

HDU 4652 Dice (概率DP)

Dice Problem Description You have a dice with m faces, each face contains a distinct number. We assume when we tossing the dice, each face will occur randomly and uniformly. Now you have T query to answer, each query has one of the following form: 0

HDU 4652 Dice (概率DP)

B - Dice Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4652 Description You have a dice with m faces, each face contains a distinct number. We assume when we tossing the dice, each face will o

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

[hdu 2102]bfs+注意INF

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 感觉这个题非常水,结果一直WA,最后发现居然是0x3f3f3f3f不够大导致的--把INF改成INF+INF就过了. #include<bits/stdc++.h> using namespace std; bool vis[2][15][15]; char s[2][15][15]; const int INF=0x3f3f3f3f; const int fx[]={0,0,1,-1};

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是