JZOJ5537.【2014东莞市选】【JSOI2014】病毒分类

6 篇文章 0 订阅
4 篇文章 0 订阅

Problem

  有n个字符串,给这些字符串分组,使得每个字符串属于且仅属于一个组。
  对于一个合法的分组,至少满足以下两个条件种的一个:
  1. 所有字符串的k前缀相同(即前k个字母相同)
  2. 所有字符串的k后缀相同(即后k个字母相同)
  你需要给这些字符串分组,使得所分的组数最少。

Input

  第一行两个整数n,k(1<=n<=5000, 1<=k<=550),分别表示字符串的数量以及题述中的参数k。
  接下来有n行,每行一个字符串,字符串的长度至少为k,且不会超过550。

Output

  第一行一个整数m,表示最少的分组数目。
接下来m行,每行的第一个整数ti表示第i个分组的字符串数量,接下来有ti个整数,表示第i个分组中的字符串编号,编号对应字符串的输入顺序。数字之间用一个空格隔开。如果分组方案不唯一,输出任意一种即可。

题解

有点难想到是网络流,
看到分组,就应该向最小割方面想一下。

构图:超级源S向所有的前缀连一条容量为1的边,
所有后缀向超级汇T连一条容量为1的边。
同一个字符串的前缀向后缀连一条正无穷的边。
意义:割了哪一条边就是选前缀或者后缀,同一个字符串不能被割开。

输出方案:看看原本连向源点的边哪些被割掉了,哪些前缀就分成一组。

code

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h> 
#define ll long long
#define N 100003
#define db double
#define P putchar
#define G getchar
#define inf 998244353
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int nxt[N*2],to[N*2],v[N*2],last[N],cur[N],tot;
int q[N],h[N],S,T,ans;
int n,m,t,id,k,p[N],a[N];
bool vis[N];

struct node
{
    char p[606];
    int len,id,ans,last;
}s[N];

bool cmp1(node a,node b)
{
    for(int i=1;i<=k;i++)
    {
        if(a.p[i]<b.p[i])return 1;
        if(a.p[i]>b.p[i])return 0; 
    }
    return a.ans<b.ans;
}

bool cmp2(node a,node b)
{
    for(int i=1;i<=k;i++)
    {
        if(a.p[a.len-i+1]<b.p[b.len-i+1])return 1;
        if(a.p[a.len-i+1]>b.p[b.len-i+1])return 0; 
    }
    return a.ans<b.ans;
}

bool cmp3(node a,node b){return a.id<b.id;}
bool cmp4(node a,node b){return a.last<b.last;}

bool diff(node a,node b)
{
    for(int i=1;i<=k;i++)
        if(a.p[i]!=b.p[i])return 1;
    return 0;
}

bool diff1(node a,node b)
{
    for(int i=1;i<=k;i++)
        if(a.p[a.len-i+1]!=b.p[b.len-i+1])return 1;
    return 0;
}

bool bfs()
{
    int head=0,tail=1;
    for(int i=0;i<=id;i++)h[i]=-1;
    q[0]=S;h[S]=0;
    while(head!=tail)
    {
        int now=q[head];head++;
        for(int i=last[now];i;i=nxt[i])
            if(v[i] && h[to[i]]==-1)
            {
                h[to[i]]=h[now]+1;
                q[tail++]=to[i];
            }
    }
    return h[T]!=-1;
}

int dfs(int x,int f)
{
    if(x==T)return f;
    int w,used=0;
    for(int i=cur[x];i;i=nxt[i])
        if(h[to[i]]==h[x]+1)
        {
            w=f-used;
            w=dfs(to[i],min(w,v[i]));
            v[i]-=w;v[i^1]+=w;
            if(v[i])cur[x]=i;
            used+=w;
            if(used==f)return f;
        }
    if(!used)h[x]=-1;
    return used;
}

void dinic()
{
    while(bfs())
    {
        for(int i=0;i<=id;i++)
            cur[i]=last[i];
        ans+=dfs(S,n);
    }
}

int dg(int k,int t)
{
    if (k==T) return t;
    int q=0;
    for(int w=last[k];w;w=nxt[w])
        if (v[w] && h[k]==h[to[w]]+1)
        {
            q=dg(to[w],min(t,v[w]));
            if(q)
            {
                v[w]-=q;
                v[w^1]+=q;
                return q;
            }
            if (h[S]>id) return 0;
        }
    a[h[k]]--;
    if (!a[h[k]]) h[S]=id+1;
    h[k]++;
    a[h[k]]++;
    return 0;
}

void ins(int x,int y,int z)
{
    nxt[++tot]=last[x];
    to[tot]=y;
    v[tot]=z;
    last[x]=tot;
}

void dfs_(int x)
{
    p[x]=1;
    for(int i=last[x];i;i=nxt[i])
        if(!p[to[i]] && v[i])
        {
            if(to[i]==705)
                p[x]=1;
            dfs_(to[i]);
        }
}

int main()
{
    tot=1;
    read(n);read(k);

    for(int i=1;i<=n;i++)
    {
        for(ch=G();ch<'A' || ch>'Z';ch=G());
        for(s[i].len=0;'A'<=ch && ch<='Z';ch=G())s[i].p[++s[i].len]=ch;
        s[i].ans=i;
    }

    S=1;T=2;
    sort(s+1,s+1+n,cmp1);id=2;

    for(int i=1;i<=n;i++)
    {
        if(diff(s[i],s[i-1]))id++;
        s[i].id=id;
    }

    for(int i=3;i<=id;i++)
        ins(S,i,1),ins(i,S,0);

    t=id;

    sort(s+1,s+1+n,cmp2);
    for(int i=1;i<=n;i++)
    {
        if(diff1(s[i],s[i-1]))id++;
        ins(s[i].id,id,inf);
        ins(id,s[i].id,0);
        s[i].last=id;
        /*for(int j=1;j<=s[i].len;j++)
            P(s[i].p[j]);
        P('\n');*/
    }

    for(int i=t+1;i<=id;i++)
        ins(i,T,1),ins(T,i,0);

    //dinic();
    a[0]=id;while(h[S]<=id)ans+=dg(S,inf);
    write(ans);P('\n');

    memset(p,0,sizeof(p));
    memset(vis,0,sizeof(vis));
    tot=0;dfs_(S);

    sort(s+1,s+1+n,cmp3);
    for(int i=1;i<=n;i++)
    {
        if(p[s[i].id])continue;
        ans=0;
        for(int j=i;j<=n && s[i].id==s[j].id;j++)ans++,vis[s[j].ans]=1;
        if(ans)
        {
            write(ans),P(' ');
            for(int j=i;j<=n && s[i].id==s[j].id;j++)write(s[j].ans),P(' ');
            P('\n');
            i=i+ans-1;
        }
    }

    sort(s+1,s+1+n,cmp4);
    for(int i=1;i<=n;i++)
    {
        if(!p[s[i].last])continue;
        ans=0;t=0;
        for(int j=i;j<=n && s[j].last==s[i].last;j++,t++)
            if(!vis[s[j].ans])ans++;
        if(ans)
        {
            write(ans),P(' ');
            for(int j=i;j<=n && s[j].last==s[i].last;j++)
                if(!vis[s[j].ans])write(s[j].ans),P(' ');
            P('\n');
        }
        i=i+t-1;
    }
}
根据引用[1],dp[u][j]表示在u子树中取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值