hdu 3663 (dancing links)精确覆盖

来源:互联网 发布:照相馆制作照片软件 编辑:程序博客网 时间:2024/06/02 14:52

题目描述:

有n个城市,还有m条边(双向),每个城市都有一个发电站,如果一个发电站工作,它能够给它所在的城市和直接相邻的城市提供电力。并且要求每个城市只能由一个发电站来提供电力(即不能够被两个或以上的发电站同时覆盖)。

然后,每个城市的发电站都有一个允许工作时间 ai bj,表示发电站只能在[ai,bi]内的某个连续区间内工作(也可以一个都不选),并且只能选一个区间(即ai = 1, bi = 5, 不能选择1-2 和4-5两个区间)。

然后给你一个数字D,问你能不能安排这n个发电站的工作时间使1~D的时间区间内,每个城市在每个时间都能够被一个发电站覆盖。

可以的话输出任意一种解决方法。

n <= 60, m<= 150, D<=5

解题报告:

初看题意,是一个覆盖问题,n又很小,搜,怎么搜,DLX的精确覆盖模型。

精确覆盖:一个0,1矩阵,选择某些行,使每一列都有且仅有一个1。即用行覆盖列。

行的定义:

一共n * 16行,16就是[1,5]区间的所有小区间:

{{1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5},

{2, 2}, {2, 3}, {2, 4}, {2, 5},

{3, 3}, {3, 4}, {3, 5},

{4, 4}, {4, 5},

{5, 5}, {0, 0}};

其中0,0表示不用。

这样第(i – 1) * 16 + j就表示第i个发电站选择小区间j时状态。

(1 <= i <= n, 1 <= j <= 16)

列的定义:

对于第(a – 1) * 16 + b行,一共有n * d + n列。

第(i – 1) * d + j列表示第i个城市的第j天 是否被这一行的状态(a发电站选择b区间)供电,

第n * d + j列为1表示这个覆盖来自第j个发电站(因为每个发电站只能用一次,所以要用额外的n列来限制,和数独那题的解法类似,由于这n列每一列只能被覆盖一次,就限制了使用次数也是1)。

这样图就建好了,套用DLX模板即可。有界的话就输出一个就好了。


#include <cstdio>  #include <cstring>  #include <iostream>  #include <cmath>  #include <algorithm>  #include <vector>  #include <bitset>  #include <queue>  #define ll long long  using namespace std;    const int maxn = 60 * 20;  const int maxm = 60 * 10;  const int max_size = maxn * maxm;  const int INF = 1e9;    int L[max_size], R[max_size], U[max_size], D[max_size], C[max_size], H[max_size];  int S[max_size], O[max_size],row[max_size];  int head, size;  int n, m, d, len;  vector<int> G[100];  int st[100], ed[100], ans[100];  bool mat[maxn][maxm];  int move[16][2] = {{1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5},                           {2, 2}, {2, 3}, {2, 4}, {2, 5},                                   {3, 3}, {3, 4}, {3, 5},                                           {4, 4}, {4, 5},                                                   {5, 5}, {0, 0}};  void init(int N,int M){      int i;      for (i = 1; i <= N; i++)               H[i] = -1;      for (i = 0; i <= M; i++) {          S[i] = 0;          L[i + 1] = i;          R[i] = i + 1;          U[i] = D[i] = i;      }      R[M] = 0;      size = M + 1;  } void Link(int r,int c){    U[size] = c;      D[size] = D[c];      U[D[c]] = size;      D[c] = size;      if (H[r] < 0)          H[r] = L[size] = R[size] = size;      else {          L[size] = H[r];          R[size] = R[H[r]];          L[R[H[r]]] = size;          R[H[r]] = size;      }      S[c]++; row[size] = r ;     C[size++] = c; }void Remove(int c) {   //删除c列及相应的行       int i, j;      R[L[c]] = R[c];      L[R[c]] = L[c];      for (i = D[c]; i != c; i = D[i]) {          for (j = R[i]; j != i; j = R[j]) {              U[D[j]] = U[j];              D[U[j]] = D[j];              S[C[j]]--;          }      }  }  void Resume(int c) {  //恢复c列       int i, j;      R[L[c]] = c;      L[R[c]] = c;      for (i = D[c]; i != c; i = D[i]) {          for (j = R[i]; j != i; j = R[j]) {              U[D[j]] = j;              D[U[j]] = j;              S[C[j]]++;          }      }  }  bool Dance(int now) {   //舞蹈链       if (R[0] == 0)  //输出答案          {  len = now;        return true;   }    int i, j, temp, c;      for (temp=INF,i = R[0]; i; i = R[i]) {          if(S[i]<temp)          {              temp=S[i];              c=i;          }      }      Remove(c);      for(i=D[c];i!=c;i=D[i])      {    O[now]=row[i]  ;   //记录答案 ;           for(j=R[i];j!=i;j=R[j])              Remove(C[j]);          if(Dance(now+1))              return true;          for(j=L[i];j!=i;j=L[j])              Resume(C[j]);      }      Resume(c);      return false;  }  int main()  {      int a, b;      while(~scanf("%d%d%d", &n, &m, &d))      {          for(int i = 1; i <= n; i++)          {              G[i].clear();              G[i].push_back(i);          }          for(int i = 0; i < m; i++)          {              scanf("%d%d", &a, &b);              G[a].push_back(b);              G[b].push_back(a);          }          for(int i = 1; i <= n; i++)          scanf("%d%d", &st[i], &ed[i]);          memset(mat, 0, sizeof(mat));          for(int i = 1; i <= n; i++)          {              for(int j = 0; j < 15; j++)              {                  int x = (i - 1) * 16 + j + 1;                  if(move[j][0] >= st[i] && move[j][1] <= ed[i])                  {                      for(int k = 0; k < (int) G[i].size(); k++)                      {                          int v = G[i][k];                          for(int l = move[j][0]; l <= move[j][1]; l++)                          mat[x][(v - 1)* d + l] = 1;                      }                      mat[x][n * d + i] = 1;                  }              }              mat[(i - 1) * 16 + 16][n * d + i] = 1;          } int N=n*16,M=n*d+n ;  //行数列数         init(N, M);          //初始化         //构造十字链表         for (int i = 1; i <= N; ++i)         for (int j = 1; j <= M; ++j) {            if (!mat[i][j]) continue;            else   Link(i,j)  ;     //插入         }        Dance(0);        //舞蹈链         if(len != n) printf("No solution\n");          else          {               for(int i = 0; i < len; i++)              {                  int tmp = ((O[i] - 1) / 16) + 1;                  int tmp2 = O[i] - (tmp - 1) * 16 - 1;                  ans[tmp] = tmp2;              }              for(int i = 1; i <= n; i++)                  printf("%d %d\n", move[ans[i]][0], move[ans[i]][1]);          }          puts("");      }      return 0;  }  



0 0
原创粉丝点击