[Codeforces Round #428 DIV2E (CF839E)] Mother of Dragons

题意

给出 n40 个点的邻接矩阵,要求给每个点赋值,使得点的权值和为 K ,每条边权值为两端点点权的乘积,最大化边的权值和。

题解

这道题我不会严谨的证明,只能猜一猜,官方题解http://codeforces.com/blog/entry/53815
首先假如整个图是一个点数为k的团,那么每个点的权值分为 K/k ,最优答案将是 K2(k1)2k
假如图中存在一个点数为 k 的团和一个点数为k+1的团,由 (k1)2k=1212k 知,选择 k+1 的团答案更优。
若存在一个点数为 k 的团,和差一条边就是k+1的团的伪团(即 k+1 个点, (k+1)k21 条边,暂时称作伪团),若给 k+1 的伪团每点赋值 Kk+1 ,则可以通过做差法求得伪团不如 k 点的团更优。
基于以上结果,猜测答案取得最优时即为点集为最大团时。
下面就是求最大团的过程了。
由于一般图求最大团是NP-hard的,做的求最大团的题目,只做过2n的搜索。这道题目点数40,显然是双向搜索。于是写了这道题的代码1436 ms
然而打开status一看,尽是15ms 30ms的代码,于是点开某30ms代码学习,发现了一个没接触过的搜索算法 BronKerbosch ,于是在网上学习了一下这个算法的姿势,发现朴素的 BronKerbosch 算法是求所有的极大团,可以求出最大团,但通过一些优化只求最大团可以有效的提高效率。其中两种优化较好,第一种优化是按照退化序枚举点的优化,也就是不断向点集中添点后求最大团,按照这个算法写了代码215ms;第二种是带轴优化,对于一个与当前团内点有边相连但不在当前团内的点 u ,最大团要么包含u,要么包含与 u 不相邻的其他点,即选定u后将备选点中与 u 相邻的点都删去(因为备选点是与当前团中点都相邻的点,如果某备选点与u相邻且被选中,那么必定也要u),所以只需要检验点 u 与和u不相邻的点即可,根据这个思想写了代码315ms
参考资料:
https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm
https://wenku.baidu.com/view/65972e184693daef5ff73d2c.html
http://www.cnblogs.com/yefeng1627/archive/2013/03/31/2991592.html

代码1

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define  kN  45LL
#define  kS  1048576LL

int n, mid, cnt[kN];
bool g[kN][kN];
bool okL[kS], okR[kS];
int siz[kS];

void dfsL(int p=0,int s=0) {
    if (p == mid) return;
    dfsL(p+1,s);
    if (s) for(int i=0;i<mid;i++) if ((s&(1<<i)) && !g[p][i]) return;
    s |= (1<<p);
    okL[s] = true;
    dfsL(p+1,s);
}
void dfsR(int p=mid,int s=0) {
    if (p == n) return;
    dfsR(p+1,s);
    if (s) for(int i=mid;i<n;i++) if ((s&(1<<i-mid)) && !g[p][i]) return;
    s |= (1<<p-mid);
    okR[s] = true;
    dfsR(p+1,s);
}
int calc(int s) {
    int i,ret=0;
    rep (i,0,mid) if (s&(1<<i)) ret ++ ;
    return ret;
}
int main() {
    int i, j, k, K, ma, sz;
    lf ans;
    r(n), r(K);
    if (n == 1) {puts("0"); goto END; }
    rep (i,0,n) rep (j,0,n) r(g[i][j]);
    mid = n>>1;
    dfsL();
    dfsR();
    ma = 0;
    for (i = (1<<mid)-1; i >= 0; i -- ) if (okL[i]) siz[i] = calc(i);
    for (i = 0; i < (1<<mid); i ++ ) if (siz[i]) {
        if (siz[i] > ma) ma = siz[i];
        rep (j,0,mid) if (!(i&(1<<j))) {
            k = i|(1<<j);
            if (siz[k] < siz[i]) siz[k] = siz[i];
        }
    }
    for (i = (1<<n-mid)-1; i >= 0; i -- ) if (okR[i]) {
        sz = 0;
        rep (j,0,mid) cnt[j] = 0;
        rep (j,mid,n) if (i&(1<<j-mid)) {
            sz ++ ;
            rep (k,0,mid) if (g[j][k]) cnt[k] ++ ;
        }
        k = 0;
        rep (j,0,mid) if (cnt[j] == sz) k |= (1<<j);
        if (siz[k]+sz > ma) ma = siz[k]+sz;
    }
    ans = 1.0*K*K*(ma-1)/ma/2.0;
    printf("%.12lf\n", ans);
    END: getchar(), getchar();
    return 0;
}

代码2

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define  kN  45LL

int n;

namespace MaxClique {
bool g[kN][kN];
int ans, adj[kN][kN], ma[kN];
bool dfs(int now,int dep) {
    int i, j, u, v, poi;
    rep (i,0,now) {
        if (dep+now-i <= ans) return false;
        u = adj[dep][i], poi = 0;
        if (dep+ma[u] <= ans) return false;
        // rec[dep] = u;
        rep (j,i+1,now) if (v=adj[dep][j], g[u][v]) adj[dep+1][poi++] = v;
        if (dfs(poi,dep+1)) return true;
    }
    if (dep > ans) {
        ans = dep; // upd max clique
        return true;
    }
    return false;
}
int work() {
    int i, j, poi;
    ans = 0;
    Rev (i,n-1,0) {
        poi = 0;
        rep (j,i+1,n) if (g[i][j]) adj[1][poi++] = j;
        //rec[0] = i;
        dfs(poi,1);
        ma[i] = ans;
    }
    return ans;
}
}

int main() {
    int K, i, j;
    lf ans;
    r(n), r(K);
    using MaxClique::g;
    using MaxClique::work;
    rep (i,0,n) rep (j,0,n) r(g[i][j]);
    i = work();
    ans = 1.0*K*K*(i-1)/i/2.0;
    printf("%.12lf\n", ans);
    END: getchar(), getchar();
    return 0;
}

代码3

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define  kN  45LL

int n;

namespace MaxClique {
using std::max;
typedef unsigned long long U;
inline int ctz(U s){return s?__builtin_ctzll(s):8*sizeof(U);}
int ans;
bool g[kN][kN];
U N[kN];
void dfs(U R,U P,U X) {
    if (!P && !X) {
        ans = max(ans,__builtin_popcountll(R));
        return ;
    }
    if (!P) return;
    int pivot = ctz(P|X);
    U PP = P&~N[pivot];
    for(int u=ctz(PP);u<n;u+=ctz(PP>>(u+1))+1) {
        dfs(R|((U)1<<u),P&N[u],X&N[u]);
        P ^= ((U)1<<u), X |= ((U)1<<u);
    }
}
int work() {
    int i, j;
    rep (i,0,n) rep (j,0,n) if (g[i][j]) N[i]^=(U)1<<j;
    dfs(0,((U)1<<n)-1,0);
    return ans;
}
}

int main() {
    int K, i, j;
    lf ans;
    r(n), r(K);
    using MaxClique::g;
    using MaxClique::work;
    rep (i,0,n) rep (j,0,n) r(g[i][j]);
    i = work();
    ans = 1.0*K*K*(i-1)/i/2.0;
    printf("%.12lf\n", ans);
    END: getchar(), getchar();
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值