2227: 金块收集
Time Limit: 1 Sec Memory Limit: 128 MB 64bit IO Format: %lldSubmitted: 10 Accepted: 1
[ Submit][ Status][ Web Board]
Description
有一个非常复杂的K叉树形迷宫(每个非叶子结点都有K个孩子),除了根节点外每个节点都有自己的标识。每个节点的标记规则:
(1)树根的孩子结点从左到右分别标记为'A','B',...
(2)其它每个结点的标记为双亲节点的标记符号串+本节点对应的字符,如果本节点是双亲结点的第i个孩子(1<=i),则本节点对应的字符为‘A'+i-1。
树根所在层为第0层,则第1层的结点从左到右分别标记为'A','B',...‘A'+K-1,
第1层结点‘B’的孩子结点从左到右分别标记为‘BA’,‘BB’,'BC'...,
第2层结点‘CA’的孩子结点从左到右则分别标记为‘CAA’,‘CAB’...
该树中,共有M个节点存放有金块,你可以在该树的任意节点位置放置一个收集金块的机器人。当机器人到位后,它会自动将树根到自己位置的路径上的所有金块全部收集,并返回树根所在处。
一旦一个节点的金块被某个机器人收集走以后,以后经过该结点的机器人就没有金块可收集了。
现在问题是:如果你可以放置N个收集金块机器人,那么你最多可以收集多少结点的金块?
Input
多组测试数据,每组测试数据描述如下:
第一行包含4个整数M,K,L和N。1<=M<=50000,1<=K<=26,1<=L<=50,1<=N<=10
M表示存放有金块的结点数。
K表示该树每个非叶子结点的孩子数。
L表示该树的深度。
N表示最多可以放置的收集金块机器人数。
接下来的M行,每行一个字符串,表示标记为该字符串的结点存放有金块
Output
输出能够收集到的最多金块数。
Sample Input
5 3 3 1
ACC
ACB
AB
AC
A
5 3 3 2
ACC
ACB
AB
AC
A
Sample Output
3
4
思路:对题目中给出的点进行建树,然后用树形DP就行了。
建树的关键是对于某个点x,要找到离它最近的祖先点y(且y要出现在题目给出的M个点中),然后y->x即y向x连一条边即可。
那么如何找到离x最近的祖先呢?当x="ABCDEFA"时,它的父节点为ABCDEF,它父节点的父节点是ABCDE.....以此类推,我们按照这样一个一个枚举x的祖先,并判断其是否存在于题目给出M个点中,如果存在,则把它向x连一条边。
那么如何快速查询x的祖先是否存在于题目给出的M个点中呢?string+map会TLE...用Trie树会MLE...于是想到了用Hash。
#include<bits/stdc++.h> using namespace std; const int MAX=5e4+10; const int MOD=1e9+7; const int Hashsize=1000007; const double PI=acos(-1.0); typedef long long ll; struct lenka { int next,index; char q[52]; }ed[MAX]; int head[Hashsize],tot; char p[55]; char s[MAX][52]; int Hash() { ll ans=0; for(int i=0,len=strlen(p);i<len;i++)ans=(ans*26+p[i]-'A')%Hashsize; return ans; } void Insert(int x) { int y=Hash(); ed[tot].next=head[y]; ed[tot].index=x; strcpy(ed[tot].q,p); head[y]=tot++; } int ask() { int y=Hash(); for(int i=head[y];i!=-1;i=ed[i].next) { if(strcmp(p,ed[i].q)==0)return ed[i].index; } return 0; } vector<int>e[MAX]; int d[MAX][11];//d[k][j]表示在k点往下派出了j个机器人所能取到的最多金块数 void dfs(int k,int n) { memset(d[k],0,sizeof d[k]); for(int i=0;i<e[k].size();i++) { int nex=e[k][i]; dfs(nex,n); for(int j=n;j>=1;j--) { for(int h=1;h<=j;h++)d[k][j]=max(d[k][j],d[k][j-h]+d[nex][h]); } } for(int i=1;i<=n;i++)d[k][i]+=1; } int main() { int M,K,L,N; while(scanf("%d%d%d%d",&M,&K,&L,&N)!=EOF) { tot=0; memset(head,-1,sizeof head); e[0].clear(); //将0设置为树根 for(int i=1;i<=M;i++) { scanf("%s",s[i]); strcpy(p,s[i]); Insert(i); //插入Hash表 e[i].clear(); } for(int i=1;i<=M;i++)//将所有点与其祖先连边 { strcpy(p,s[i]); int len=strlen(p)-1; while(1) { p[len]='\0'; len--; if(len==-1)//找不到祖先,直接将其与根节点相连 { e[0].push_back(i); break; } else { int x=ask(); if(x) { e[x].push_back(i); break; } } } } dfs(0,N); printf("%d\n",d[0][N]-1); } return 0; }