CF1105E Helping Hiasat

最大独立集

神仙题,感觉不止2200的难度

先要建图

可以发现无论有多少个连续的1操作,都是可以看做1次1操作

那么可以将以这1作为分组的界限,将连续的2操作分为一组

然后将每一个朋友姓名的字符串用map哈希成数字

此时将每一个朋友在哪一组出现过都处理出来

称两个朋友是冲突的,当且仅当这两个朋友同时出现在任意一组中

那么将这两个朋友之间连边,那么图就建出来了

 

比赛时我就想到这里,然后用拓扑序乱搞,竟然水过了前29个点

后来jzy大佬,喊了一声最大独立集,我就恍然大悟

 

然后答案就是这张图的最大独立集

最大独立集就是原图补图的最大团

因为m是40的范围,直接暴力会超时,所以要有dp优化

MYY大佬是折半搜索的(好吧我不想写

当然,如果会高级算法也能直接过(然而我不会

#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define ll long long
using namespace std;
const int MAXN=100000+10;
int n,m,w,si[51],vi[51],ans,g;
int MIN,fx[51][51],dp[51],s[51];
map <string,int> mp;
struct node
{
    int op;
    string name;
}sh[MAXN];
vector <int> f[MAXN],ti[51],e[51];
set <pair<int,int> > q;
bool check(int x,int y)
{
    for (int i=0;i<(int)ti[x].size();i++)
    {
        vector <int> :: iterator it;
        it=lower_bound(ti[y].begin(),ti[y].end(),ti[x][i]);
        if (it==ti[y].end())
          continue;
        if (*it==ti[x][i])
          return true;
    }
    return false;
}
bool judge(int x,int end)
{
    for (int i=1;i<end;i++)
    {
        if (!fx[s[i]][x])
          return false;
    }
    return true;
}
void dfs(int x,int wh)//暴力求出补图的最大团
{
    if (x+m-wh+1<=ans || x+dp[wh]<=ans)
      return;
    for (int i=wh;i<=m;i++)
    {
        if (judge(i,x+1))
        {
            s[x+1]=i;
            dfs(x+1,i+1);
        }
    }
    if (x>ans)
      ans=x;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&sh[i].op);
        if (sh[i].op==2)
        {
            cin>>sh[i].name;
            if (mp[sh[i].name]==0)
            {
                w++;
                mp[sh[i].name]=w;//离散化
            }
        }
    }
    int bl=-1;
    w=0;
    for (int i=1;i<=n;i++)//分组
    {
        if (bl==-1 && sh[i].op==2)
        {
            w++;
            bl=0;
            f[w].push_back(mp[sh[i].name]);
        }
        else
        if (bl==0 && sh[i].op==2)
        {
            f[w].push_back(mp[sh[i].name]);
        }
        else
        if (bl==0 && sh[i].op==1)
          bl=-1;
    }
    for (int i=1;i<=w;i++)
    {
        for (int j=0;j<(int)f[i].size();j++)
          ti[f[i][j]].push_back(i);
    }
    for (int i=1;i<=m;i++)
    {
        for (int j=i+1;j<=m;j++)
        {
            if (check(i,j))
            {
                e[i].push_back(j);
                e[j].push_back(i);
            }
        }
    }
    for (int i=1;i<=m;i++)
    {
        for (int j=1;j<=m;j++)
          fx[i][j]=1;
    }
    for (int i=1;i<=m;i++)
    {
        for (int j=0;j<(int)e[i].size();j++)
          fx[i][e[i][j]]=0;//建出原图的补图
        fx[i][i]=0;
    }
    dp[m]=1;
    ans=0;
    for (int i=m-1;i>=1;i--)//求出补图的最大团
    {
        s[1]=i;
        dfs(1,i+1);
        dp[i]=ans;
    }
    printf("%d\n",dp[1]);
}

 

转载于:https://www.cnblogs.com/huangchenyan/p/11258414.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值