WUST OJ 2227:金块收集(Hash+树形DP)

2227: 金块收集

Time Limit: 1 Sec   Memory Limit: 128 MB   64bit IO Format: %lld
Submitted: 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;
}



猜你喜欢

转载自blog.csdn.net/mitsuha_/article/details/80438892
OJ