【HDU 3663】 Power Stations 【精确覆盖】

题意:

有n个村庄,每个村庄有个发电机,村庄之间有路,如果一个村庄开了发电机,那么和该村庄有边直接相连的就可以获得能量(他自己当然也有),并且一个村庄不能获得太多能量,也就是说在且只在一个发电机的供电范围内,然后每个村庄的发电机有一个能工作的时间区间,让你做一个规划,使得每个村庄每天都有且只有一个能量供应

题解:

这个题目的构图思路比较难想,但一个村庄每次只被覆盖一次是很容易想到可以用dlx算法来实现精确覆盖

然后我们来构造一个矩阵来实现题目要求

行:n*16,我们注意到时间是1<=D<=5,这样我们可以枚举每个村庄的工作时间[0,0],[1,1],[1,2],[1,3],,,,[4,5],[5,5]这样一共有16种情况,这样每一行都代表一个村庄选取一个区间时候的状态

列:n*d+n列,首先一个村庄选取一个区间时,我们就把在这个区间内和这个村庄相连的位置都标上1,n*d代表的是n个村庄n天的状态,然后剩下的n列是用来限制只能选取一个区间的,我们在属于村庄i的16行的第n*d+i列都标上1,这样由于精确覆盖的特性,只会选择其中一个

真的是很神奇的构图,顺便搞了个精确覆盖模板

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define PB push_back
#define MP make_pair
#define ll long long
#define MS(a,b) memset(a,b,sizeof(a))
#define LL (rt<<1)
#define RR (rt<<1|1)
#define lson l,mid,LL
#define rson mid+1,r,RR
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lb(x) (x&(-x))
void In(){freopen("in.in","r",stdin);}
void Out(){freopen("out.out","w",stdout);}
const int N=1e2+10;
const int M=1e6+10;
const int Mbit=1e6+10;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const double eps=1e-8;
int ans[N];
bool vis[N][N];
struct DLX
{
    const static int maxn=60*20;
    const static int maxm=60*10;
    const static int X=1e5+10;
    int n,m,siz;
    int H[maxn],S[maxm];
    int U[X],D[X],L[X],R[X],col[X],row[X];
    void init(int _n,int _m)
    {
        n=_n;m=_m;
        for(int i=0;i<=m;i++){
            S[i]=0;
            U[i]=D[i]=i;
            L[i]=i-1;
            R[i]=i+1;
        }
        R[m]=0;L[0]=m;
        siz=m;
        for(int i=1;i<=n;i++)
            H[i]=-1;
    }
    void link(int r,int c)
    {
        ++S[col[++siz]=c];
        row[siz]=r;
        D[siz]=D[c];
        U[D[c]]=siz;
        U[siz]=c;
        D[c]=siz;
        if(H[r]<0)H[r]=L[siz]=R[siz]=siz;
        else{
            R[siz]=R[H[r]];
            L[R[H[r]]]=siz;
            L[siz]=H[r];
            R[H[r]]=siz;
       }
    }
    void remove(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[col[j]];
            }
    }
    void resume(int c)
    {
        for(int i=U[c];i!=c;i=U[i])
            for(int j =L[i];j!=i;j=L[j])
                ++S[col[U[D[j]]=D[U[j]]=j]];
        L[R[c]]=R[L[c]]=c;
    }
    bool dance(int d)
    {
        if(R[0]==0)return true;
        int c=R[0];
        for(int i=R[0];i!=0;i=R[i])
            if(S[i]<S[c])
                c=i;
        remove(c);
        for(int i=D[c];i!=c;i=D[i]){
            ans[(row[i]-1)/16+1]=(row[i]-1)%16;
            for(int j=R[i];j!=i;j=R[j])remove(col[j]);
            if(dance(d+1))return true;
            for(int j=L[i];j!=i;j=L[j])resume(col[j]);
        }
        resume(c);
        return false;
    }
}dlx;
int arr[][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}};
vector<int>G[N];
int main()
{
    //In();
    int d,n,m;
    while(~scanf("%d%d%d",&n,&m,&d)){
        MS(vis,0);
        for(int i=1;i<=n;i++){
            G[i].clear();
            G[i].PB(i);
        }
        for(int i=1;i<=m;i++){
            int a,b;scanf("%d%d",&a,&b);
            if(vis[a][b])continue;
            vis[a][b]=vis[b][a]=1;
            G[a].PB(b);
            G[b].PB(a);
        }
        dlx.init(n*16,n*d+n);
        for(int i=1;i<=n;i++){//第i个点
            int s,e;scanf("%d%d",&s,&e);
            for(int j=0;j<15;j++){
                if(arr[j][0]>=s&&arr[j][1]<=e){//区间[arr[j][0],arr[j][1]]是开着的
                    for(int k=0;k<G[i].size();k++){
                        int x=G[i][k];//x是和i相连的点
                        for(int l=arr[j][0];l<=arr[j][1];l++){
                            dlx.link((i-1)*16+j+1,(x-1)*d+l);
                        }
                    }
                    dlx.link((i-1)*16+j+1,n*d+i);
                }
                dlx.link((i-1)*16+16,n*d+i);
            }
        }
        if(dlx.dance(0)){
            for(int i=1;i<=n;i++)
            printf("%d %d\n",arr[ans[i]][0],arr[ans[i]][1]);
        }
        else puts("No solution");puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值