2016香港网络赛G题 未ac

Problem G

k-Colouring of a Graph

You are given a simple graph with N nodes and M edges. The graph has the special property that any connected component of size s contains no more than s+2 edges. You are also given two integers k and P. Find the number of k-colourings of the graph, modulo P.

Recall that a simple graph is an undirected graph with no self loops and no repeated edges. A k-colouring of a graph is a way to assign to each node of the graph exactly one of k colours, such that if edge (u,v) is present in the graph, then u and v receive different colors.

Input

The first line of input consists of four integers, N,M,k, and P (1≤N≤50000, 0≤M≤1.5N, 1≤k≤109, 1≤P≤2⋅109). The next M lines of input each contains a pair of integers A and B (1≤A≤N, 1≤B≤N), describing an edge in the graph connecting nodes A and B.

Output

Output the number of k-colourings of the given graph, modulo P.

Sample Input 1
3 3 2 10000
1 2
2 3
3 1
Sample output 1
0
Sample Input 2
3 3 4 13
1 2
2 3
3 1
Sample output 2
11

题意

N个节点M条边的图着k种颜色(其中M<=N+2),使得每条边连接的两个点是不同的颜色,求着色方案的数量。

这个问题本身是一个NP问题,应该用回溯法来解决。但是网上的这个模板改出来之后TLE,关了输入输出流同步之后还是TLE。我猜应该是这种方法写的…只是需要再优化。不然就是某个鬼畜的数学方法。

下面的超时代码:用邻接表c来表示一个无向连通图G=(V,E)。用整数1,2,…,m来表示m种不同的颜色。x[i]表示顶点i所着的颜色来,则问题的解向量可以表示为n元组x[1:n]。问题的解空间可表示一棵高度为n+1的完全m叉树。解空间树的第i层中每一结点都有m个儿子,每个儿子相应于x[i]的m个可能的着色之一,第n+1层结点均为叶结点。

在回溯算法中,当i>n时,表示算法已搜索至一个叶结点,得到一个新的m着色方案,因此当前已找到的可m着色方案数sum增1。当i≤n时,当前扩展结点Z是解空间树中的一个内部结点。该结点有x[i]=1,2,…,m。对当前扩展结点Z的每一个儿子结点,由函数Ok检查其可行性,并以深度优先的方式递归地对可行子树进行搜索,或剪去不可行子树。

TLE代码

#include <iostream>
#include <vector>
#include <cstdio>
#include <algorithm>

#define ll long long

std::vector <int> c[50010];

ll point_color[5010];
ll cnt = 0;
ll n, m, u, v, g, p;

bool ok(int k) {
    for(int i = 1; i < k; i++){
        std::vector<int>::iterator result = find(c[k].begin( ), c[k].end( ), i); 
        if(!(result == c[k].end()) && point_color[i] == point_color[k]) {
            return 0;
        }
    }
    return 1;
}

void graphcolor(int n, int m) {
    for(int i = 1; i <= n; i++)
        point_color[i] = 0;
    int k = 1;
    while(k >= 1) {
        point_color[k] = point_color[k] + 1;
        while(point_color[k] <= m){
            if (ok(k)) {
                break;
            } else {
                point_color[k]=point_color[k]+1;
            }
        }
        if(point_color[k] <= m && k == n) {         
            cnt++;
            cnt %= p;
        }
        else if(point_color[k] <= m && k < n) {
            k = k + 1;
        } else {
            point_color[k] = 0;
            k = k - 1;
        }
    }
}


int main(int argc, char *argv[]) {
    std::ios::sync_with_stdio(false); 
    std::cin >> n >> g >> m >> p;
    int s, t;
    for(int i=1; i <= g ; i++) {
        std::cin >> s >> t;
        c[s].push_back(t);
        c[t].push_back(s);
    } 
    graphcolor(n, m);
    std::cout << cnt << std::endl;
    return 0;
}

UPDATE

时隔一年之后群里问了这题,qls说是bell(6)暴力枚举三条非树边六个节点的颜色,然后树DP。
复习阶段 好像搞不动 先鸽着。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值