2018.9.8 2018NOIP冲刺之配对

时间:2022-12-16 17:50:25

普及组第四题难度

主体思路竟然是贪心Q_Q


链接:https://www.nowcoder.com/acm/contest/164/D
来源:牛客网

题目描述

小A有n个长度都是L的字符串。这些字符串只包含前8个小写字符,'a'~'h'。但这些字符串非常的混乱,它们几乎长得互不相同。小A想通过一些规则,让它们长得尽可能相同。小A现在有K次机会,他可以每次机会,可以选择一对字符x,y,让x,y变成等价的字符(注意这里x,y和字符'x', 'y'不是一样的,只是个代号)。注意,等价关系是有传递性的。比如小A让'a'和'b'等价, 'b'和'c'等价,那么'a'和'c'等价。
对于两个长度字符串P,Q是等价的,当且仅当对于每一位,P的字符和Q的字符都是等价的。
小A希望你告诉他,要怎么利用好这K次机会(当然可以不用完),使得尽可能多对字符串是等价的。注意每对字符串只能算一次。

输入描述:

第一行输入三个整数n,L,K。
接下来n行,每行给出一个长度为L的字符串。

输出描述:

输出一行一个整数,代表最多可能的等价的字符串对。
示例1

输入

5 4 2
ccdd
babd
bdcd
ccda
bacd

输出

4

说明

让字符a和字符d等价,字符b和字符c等价。

备注:

数据包含10个数据点。每个数据点可能有不同的特性。
对于第1,2个数据点: 保证每个字符串只包含前4个小写字母
对于第3,4个数据点:每个字符串都只包含一种字母
对于第5,6个数据点:n<=10,L<=100
对于所有数据,满足:n <= 100, L <= 1000,K <= 28,每个字符串只包含前8个小写字母

最开始考虑的是dp 然鹅发现不能做
后来打dfs只得了20
其实如果从骗分的角度来想就可以知道是贪心(就是先同化大的嘛)
证明就不证明了应该很好理解

本题还存在一个难点就是如何解决传递性的问题
于是我们可以发现一个东西叫作并查集可以把所有的数字放在一个根节点下面
同时我们可以用一个叫作拓扑排序的东西进行贪心
上代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<map>
using namespace std;
#define int long long
#define olinr return
#define _ 0
#define love_nmr 0
#define DB double
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            f=-f;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline void put(int x)
{
    if(x<0)
    {
        x=-x;
        putchar('-');
    }
    if(x>9)
        put(x/10);
    putchar(x%10+'0');
}
int n;
int L;
int k;
struct node
{
    char x;
    char y;
    int dis;
    friend bool operator < (const node &a,const node &b)
    {
        return a.dis>b.dis;
    }
}edge[1050];
int cnt;
char a[150][1050];
int dis[50][50];
int fa[56];
bool vis[150][150];
inline int findset(int x)
{
    return x==fa[x]? fa[x]:fa[x]=findset(fa[x]);
}
int ans;
inline bool can(int i,int j)
{
    for(int k=1;k<=L;k++)
        if(findset(a[i][k]-'a')!=findset(a[j][k]-'a')) return false;
    return true;
}
inline void lb()
{
    for(int i=0;i<=25;i++)
        for(int j=i+1;j<=25;j++)
            if(dis[i][j])
            {
                cnt++;
                edge[cnt].x=i+'a';
                edge[cnt].y=j+'a';
                edge[cnt].dis=dis[i][j];
            }
}
signed main()
{
    n=read();
    L=read();
    k=read();
    for(int i=1;i<=n;i++)
        scanf("%s",a[i]+1);
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            for(int k=1;k<=L;k++)
            {
                if(a[i][k]!=a[j][k])
                {
                    dis[a[i][k]-'a'][a[j][k]-'a']++;
                    dis[a[j][k]-'a'][a[i][k]-'a']++;
                }
            }
        }
    }
    lb();
    for(int i=0;i<=50;i++)
        fa[i]=i;
    int tot=0;
    sort(edge+1,edge+cnt+1);
    for(int i=1;i<=cnt;i++)
    {
        int x=edge[i].x-'a';
        int y=edge[i].y-'a';
        int xx=findset(x);
        int yy=findset(y);
        if(xx!=yy)
        {
            fa[xx]=yy;
            tot++;
        }
        if(tot==k) break;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            if(vis[i][j]||vis[j][i]) continue;
            if(can(i,j))
            {
                ans++;
                vis[i][j]=vis[j][i]=true;
            }
        }
    }
    put(ans);
    olinr ~~(0^_^0)+love_nmr;
}