【Algorithm】 着色

来源:互联网 发布:网络传销案 编辑:程序博客网 时间:2024/06/05 09:00

题目:

图由N个点M条边组成,一条边连接2个点, 给图中的点着色,只能是黑色或白色,有一个条件,共享一条边的两个点不能同时为黑色

这里的图是一颗树,不存在环路,无向

思路:

正常思路就是递归,先给一个点着色,然后检查这个点之前的点的颜色是否冲突,不冲突继续着色,直到所有的点都有颜色。然后递归,换一个颜色,继续着色。

int ok(int x){           if (color[parent[x]]==1)    {        return 0;    }    return 1;}void dfs(int t){    if (t==0)    {        Answer++;    }    else    {               color[t] = 0;        dfs(t - 1);        color[t] = 1;               if (ok(t))        {            color[parent[t]] = 1;            if (color[t-1] == -1)            {                dfs(t - 1);            }                   }                   color[t] = -1;    }}

这个当时是可以解决问题的,但是递归的效率不高,当数据量很大的时候,就无法满足。
这里提供一个新的思路。

这里写图片描述

这里写图片描述

通过上图我们可以看出,节点之间种类的关系,可以得出所有叶子节点的着白色次数为1,着黑色次数为1,下一步是计算父亲节点的着色次数。当一个节点所有的孩子的着色次数计算完成后,即可得到该节点的着色次数。那么树的根节点的着色次数,就是我们最终要求的节点个数。
这里我们可以创建这样一个数据结构来解决问题。
Head数组中存放所有节点,而每个节点都有一个链表来存其孩子节点。当我们要计算Head[i]的着色次数,就是将其链表节点所有的着色次数分别求出,然后按照公式相乘,就可以得到数组中节点的着色次数。
我们可以在链表结点中保存数组下标,就可以知道这个孩子是否是其他节点的父亲节点,从而来求解节点的着色次数。
如图中所示,假设head数组中第一个元素是0,有2,3两个孩子,那个2,3,在数组中又是其他节点的父亲,必须算到叶子节点之后,才能算出2,3的着色次数。

#include <stdio.h>#define MAX 100001#define MOD 1000000007int Answer = 0;int N;typedef struct NODE{    int id;    NODE* next;}Node;typedef struct HEAD{    NODE* next;}Head;Head data[MAX];long long w[MAX];long long b[MAX];void solve(int index, int pre){    Head p = data[index];       Node* t = p.next;    while (t)    {        int child = t->id;        if (child != pre)        {            solve(child, index);            w[index] = w[index] * (w[child] + b[child]) % MOD;            b[index] = b[index] * w[child] % MOD;        //  printf("data[%d]'s w=[%d] b=[%d]\n", index, w[index], b[index]);        }        t = t->next;    }}void addnode(int v1, int v2){    Node* p = new NODE;    p->id = v2;    p->next = NULL;    Node* q = data[v1].next;    if (q == NULL)    {        data[v1].next = p;    }    else    {        data[v1].next = p;        p->next = q;    }}int main(void){    int test_case;    int T;    freopen("input.txt", "r", stdin);    setbuf(stdout, NULL);    scanf("%d", &T);//  T = 1;    for (test_case = 1; test_case <= T; ++test_case)    {           Answer = 0;        scanf("%d",&N);        int i ;             for (i = 1; i <= N; i++)        {            w[i] = 1;            b[i] = 1;        //  data[i].next = NULL;            Node* t = data[i].next;            while (t)            {                data[i].next = t->next;                delete t;                t = data[i].next;            }            data[i].next = NULL;        }        for ( i = 1; i <= N-1; i++)        {            int v1, v2;            scanf("%d%d",&v1,&v2);              addnode(v1,v2);            addnode(v2,v1);        }    //  for (int i = 1; i < N; ++i){    //      printf("%d\n", data[i].next->id);    //  }        solve(1,-1);        Answer = (b[1] + w[1]) % MOD;        printf("#%d %lld\n", test_case, Answer);            }    return 0;}

代码中需要注意的细节很多,比如给head数组的元素添加一个节点时,不用插入到链表最后,而是就近原则,不然性能会很差
还有就是计算着色次数的时候,不能将自己算进去,所以要判断id!=pre, 比如计算2的时候,2作为元素0的node,需要计算,但是作为head的元素2的时候,不能计算。