hdu 3663 Power Stations(精确覆盖 Dancing Links 模版)

本文介绍了一个电力供应调度问题,通过DLX算法实现精确覆盖求解。具体问题为给定若干城市及发电站可用时间范围,在满足每个城市只能由一个发电站供电的情况下,寻找可行的供电方案。文章详细解释了如何构建DLX算法所需的0-1矩阵,并给出了一段完整的AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Power Stations

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1899    Accepted Submission(s): 514
Special Judge


Problem Description
There are N towns in our country, and some of them are connected by electricity cables. It is known that every town owns a power station. When a town’s power station begins to work, it will provide electric power for this town and the neighboring towns which are connected by cables directly to this town. However, there are some strange bugs in the electric system –One town can only receive electric power from no more than one power station, otherwise the cables will be burned out for overload.

The power stations cannot work all the time. For each station there is an available time range. For example, the power station located on Town 1 may be available from the third day to the fifth day, while the power station on Town 2 may be available from the first day to the forth day. You can choose a sub-range of the available range as the working time for each station. Note that you can only choose one sub-range for each available range, that is, once the station stops working, you cannot restart it again. Of course, it is possible not to use any of them.

Now you are given all the information about the cable connection between the towns, and all the power stations’ available time. You need to find out a schedule that every town will get the electricity supply for next D days, one and only one supplier for one town at any time.
 

Input
There are several test cases. The first line of each test case contains three integers, N, M and D (1 <= N <= 60, 1 <= M <= 150, 1 <= D <= 5), indicating the number of towns is N, the number of cables is M, and you should plan for the next D days.

Each of the next M lines contains two integers a, b (1 <= a, b <= N), which means that Town a and Town b are connected directly. Then N lines followed, each contains two numbers si and ei, (1 <= si <= ei <= D) indicating that the available time of Town i’s power station is from the si-th day to the ei-th day (inclusive).
 

Output
For each test case, if the plan exists, output N lines. The i-th line should contain two integers ui and vi, indicating that Town i’s power station should work from the ui-th day to vi-day (inclusive). If you didn’t use this power station at all, set ui = vi = 0.

If the plan doesn’t exist, output one line contains “No solution” instead.

Note that the answer may not be unique. Any correct answers will be OK.

Output a blank line after each case.
 

Sample Input
  
3 3 5 1 2 2 3 3 1 1 5 1 5 1 5 4 4 5 1 2 2 3 3 4 4 1 1 5 1 5 1 5 1 5
 

Sample Output
  
1 5 0 0 0 0 No solution

题目描述:

有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模板即可。有界的话就输出一个就好了。

AC代码:
#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], CH[max_size], RH[max_size];
int S[max_size], O[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}};
int node(int up, int down, int left, int right) {
    U[size] = up, D[size] = down;
    L[size] = left, R[size] = right;
    D[up] = U[down] = R[left] = L[right] = size;
    return size++;
}

void init(int N, int M) {
    size = 0;
    head = node(0, 0, 0, 0);
    for (int j = 1; j <= M; ++j) {
        CH[j] = node(size, size, L[head], head), S[j] = 0;
    }
    for (int i = 1; i <= N; ++i) {
        int row = -1, k;
        for (int j = 1; j <= M; ++j) {
            if (!mat[i][j]) continue;
            if (row == -1) {
                row = node(U[CH[j]], CH[j], size, size);
                RH[row] = i, CH[row] = CH[j], ++S[j];
            } else {
                k = node(U[CH[j]], CH[j], L[row], row);
                RH[k] = i, CH[k] = CH[j], ++S[j];
            }
        }
    }
}
void remove(const int &c) {
    L[R[c]] = L[c], R[L[c]] = R[c];
    for (int i = D[c]; i != c; i = D[i]) {
        for (int j = R[i]; j != i; j = R[j]) {
            U[D[j]] = U[j], D[U[j]] = D[j];
            --S[CH[j]];
        }
    }
}
void resume(const int &c) {
    for (int i = U[c]; i != c; i = U[i]) {
        for (int j = L[i]; j != i; j = L[j]) {
            ++S[CH[j]];
            U[D[j]] = D[U[j]] = j;
        }
    }
    L[R[c]] = R[L[c]] = c;
}
bool dance(const int &k) {
    if (R[head] == head) {
        len = k - 1;
        return true;
    }
    int s = INF, c;
    for (int t = R[head]; t != head; t = R[t]) {
        if (S[t] < s) s = S[t], c = t;
    }
    remove(c);
    for (int i = D[c]; i != c; i = D[i]) {
        O[k] = RH[i];
        for (int j = R[i]; j != i; j = R[j]) {
            remove(CH[j]);
        }
        if (dance(k + 1)) {
            return true;
        }
        for (int j = L[i]; j != i; j = L[j]) {
            resume(CH[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;
        }
        init(n * 16, n * d + n);
        dance(1);
        if(len != n) printf("No solution\n");
        else
        {
             for(int i = 1; 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值