Topcoder SRM 693 & Atcoder RC#56 小结

2016-06-26 12:17:54

近期打的两场比赛,小结一下里面有趣的题。

1:TC SRM 693 div1 600pt

题意:给出一个k(<=1e9),让你造一个n(<=20)对点的二分图,点之间可以有重边,总边数为E(<=120),使得其完备匹配方案数为k。

思路:首先注意到可以有重边,这个很关键。其次,这种构造显然是拆分 k 为某个 base 进制,由于只有20对点,考虑用3进制,接下来yy一下。

※ 如下建图,边权即为该边建立的重边数。

※ 那么最右下角的黑点,就可以决定各个进制为上的数字了。

※ 比如最右下黑点与最左下白点建立 a 条边,就能构造出 a * base^3,以此类推。

※ 可以发现一旦最右下黑点与第 i 个白点匹配,那么 i+1 ~ n 号白点只能沿着斜向的边匹配 i ~ n-1 号黑点,而 1 ~ i-1 号白点可以匹配对应的黑点,形成 base^(i-1)。因此进制之间不冲突,形成加法关系。

 

class BipartiteConstruction {
    public:
    vector <int> construct(int K) {
        vector<int> ans;
        ans.PB(20);
        for(int i = 0; i < 19; ++i) 
            for(int j = 0; j < 3; ++j) ans.PB(i * 20 + i);
        for(int i = 1; i < 20; ++i) ans.PB(i * 20 + i - 1);
        for(int i = 0; i < 20; ++i){
            int v = K % 3;
            for(int j = 0; j < v; ++j) ans.PB(i * 20 + 19);
            K /= 3;
        }
        return ans;
    }
};
View Code

 

2:Atcoder RC#56 C题

题意:给你n(<=17)个人和一个常数K,第 i 个人与第 j 个人之间有合作值 wij,让你给他们分组,使得最后的评价最高,评价计算方法:分的组数 * K - ∑(wij | i 和 j 不在同一组)

思路:ok,n 很小,考虑状压,一开始我的考虑是二维,但发现比较繁琐而且没有必要。所以定义 dp[s] 为 s 状态下的最优解,s 代表已经给哪些人安排了分组。

※转移: dp[s] = dp[f] + Val(f ^ s) + K,f 是 s 的真子集,f ^ s 表示集间差(表示新的一组取的人),Val( ts )计算的是集合 ts 内每个人两两之间的合作值和。

※用记忆化来做的话需要枚举真超集,for(int f = (s + 1) | s; f < (1 << N); f = (f + 1) | s),可以用方便的超集枚举法。

int N,K;
int G[20][20];
int P[MAXN];
int dp[MAXN];
 
int Solve(int s){
    if(s == (1 << N) - 1) return 0;
    if(dp[s]) return dp[s];
    int ans = 0;
    for(int f = (s + 1) | s; f < (1 << N); f = (f + 1) | s){
        int ts = s ^ f;
        ans = max(ans,K + Solve(f) + P[ts]);
    }
    return dp[s] = ans;
}
 
int main(){
    int sum = 0;
    scanf("%d%d",&N,&K);
    for(int i = 0; i < N; ++i){
        for(int j = 0; j < N; ++j){
            scanf("%d",&G[i][j]);
            sum += G[i][j];
        }
    }
    sum /= 2;
    int top = 1 << N;
    for(int s = 0; s < top; ++s){
        for(int i = 0; i < N; ++i) if(s & (1 << i)){
            for(int j = i + 1; j < N; ++j) if(s & (1 << j)){
                P[s] += G[i][j];
            }
        }
    }
    printf("%d\n",Solve(0) - sum);
    return 0;
}
View Code

 

 

posted @ 2016-06-26 12:49  Naturain  阅读(377)  评论(0编辑  收藏  举报