题目描述:
有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;
}