hdu 3663 (dancing links)精确覆盖

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

题目描述:

有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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 2018qq密码忘了怎么办 我qq密码忘记了怎么办 微信怎么办该改密码 微信改密码收不到验证码怎么办 微信不能改密码怎么办 qq钱包被限额了怎么办? 注册战网手机号被使用怎么办 电信充值卡密码刮花了怎么办 油卡充值卡密码刮花了怎么办 电费充值卡密码刮花了怎么办 手机充值卡密码刮坏了怎么办 办中石化油卡怎么办 移动代充q币没到怎么办 电信手机话费充多了怎么办 微信钱包提现提错银行卡怎么办 qq余额提现不了怎么办 qq钱包充错话费了怎么办 苹果账户扣了钱怎么办 苹果平板冲不进去电怎么办 qq红包输了钱怎么办 qq红包实名认证没有银行卡怎么办 扣扣红包发不了怎么办 qb充错账号了怎么办 q币冲错了号了怎么办 微信qb冲错号码怎么办 微信qb冲错了怎么办 qb冲到小号了怎么办 无意中充了q币怎么办 在绝地求生里打不开充值页面怎么办 电脑版迷你世界打不开怎么办 电脑的腾讯视频打不开怎么办 好多程序连不上网了怎么办 掌游宝炉石传说卡组复制不了怎么办 花呗不能充话费怎么办 想用话费充王者怎么办 苹果6s激活出错怎么办 联通话费充多了怎么办? 电信宽带充值充到别人账号了怎么办 未实名的支付宝钱转不出来怎么办 手机打游戏闪屏怎么办 支付宝手机冲错怎么办