dancing links x(舞蹈链算法)

舞蹈链算法详解

ZOJ 3209 题目链接

题意

给定P个完整矩形的小矩形,找到最少的可以填充原来完整矩阵的矩形数

思路

DLX(精准覆盖)算法,把p个矩形当成行,把每一小格当成列(有nm小格),然后填充精确填充nm列即可

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 505;
const int M = 905;
struct node{
    int n,m,id;
    int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
    int H[N];/// 行头结点
    int S[M];
    int ansd,ans[N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c){
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    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]){
                U[D[j]] = D[U[j]] = j;
                S[Col[j]]++;
            }
        }
        L[R[c]] = R[L[c]] = c;
    }
    void dance(int deep){
        if (ansd != -1 && ansd <= deep) return ;
        if (R[0] == 0){
            if (ansd == -1) ansd = deep;
            ansd = min(ansd,deep);
            return ;
        }
        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[deep] = Row[i];
            for (int j = R[i]; j != i; j = R[j]){
                Remove(Col[j]);
            }
            dance(deep + 1);
            for (int j = L[i]; j != i; j = L[j]){
                Resume(Col[j]);
            }
        }
        Resume(c);
    }
}DLX;
int pos[33][33];
void get_pos(int n,int m)
{
    int cnt = 1;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            pos[i][j] = cnt++;
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m,p;
        scanf("%d %d %d",&n,&m,&p);
        DLX.init(p,n * m);
        get_pos(n,m);
        for (int r = 1; r <= p; r++){
            int x1,y1,x3,y3; 
            scanf("%d %d %d %d",&x1,&y1,&x3,&y3);
            for (int i = x1 + 1; i <= x3; i++){
                for (int j = y1 + 1; j <= y3; j++){
                    DLX.link(r,pos[i][j]);
                }
            }
        }
        DLX.ansd = -1;
        DLX.dance(0);
        printf("%d\n",DLX.ansd);
    }
    return 0;
}

HDU 2295 题目链接

题意

n个城市,m个雷达,雷达有限制,问雷达的最小半径是多少可以覆盖所有城市

思路

雷达为行,城市为列,DLX重复覆盖,二分查找半径跑一遍DLX
DLX中的calc类似A*算法的评估函数

#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int N = 66;
const int M = 66;
int limit;
struct node{
    int n,m,id;
    int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
    int H[N];/// 行头结点
    int S[M];
    bool v[N * M];
    int ansd,ans[N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c)
    {
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    void Remove(int c)
    {
        for (int i = D[c]; i != c; i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void Resume(int c)
    {
        for (int i = U[c]; i != c; i = U[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int calc()
    {
        int cnt = 0;
        for (int i = R[0]; i != 0; i = R[i]){
            v[i] = true;
        }
        for (int i = R[0]; i != 0; i = R[i]){
            if (v[i]){
                cnt++;
                v[i] = false;
                for (int j = D[i]; j != i; j = D[j]){
                    for (int k = R[j]; k != j; k = R[k]){
                        v[Col[k]] = false;
                    }
                }
            }
        }
        return cnt;
    }
    bool dance(int deep)
    {
        if (deep + calc() > limit) return false;
        if (deep > limit) return false;
        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;
            }
        }
        for (int i = D[c]; i != c; i = D[i]){
            Remove(i);
            for (int j = R[i]; j != i; j = R[j]){
                Remove(j);
            }
            if (dance(deep + 1)) return true;
            for (int j = L[i]; j != i; j = L[j]){
                Resume(j);
            }
            Resume(i);
        }
        return false;
    }
}DLX;
struct Point{
    double x,y;
}city[55],radar[55];
double dist(int i,int j)
{
    return sqrt((city[i].x - radar[j].x) * (city[i].x - radar[j].x) + (city[i].y - radar[j].y) * (city[i].y - radar[j].y));
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d %d %d",&n,&m,&limit);
        for (int i = 1; i <= n; i++){
            scanf("%lf %lf",&city[i].x,&city[i].y);
        }
        for (int j = 1; j <= m; j++){
            scanf("%lf %lf",&radar[j].x,&radar[j].y);
        }
        double l = 0,r = 1e5;
        while(r - l >= eps){
            double mid = (l + r) / 2.0;
            DLX.init(m,n);
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= m; j++){
                    if (dist(i,j) <= mid){
                        DLX.link(j,i);
                    }
                }
            }
            if (DLX.dance(0)) r = mid;
            else l = mid;
        }
        printf("%.6f\n",r);
    }
    return 0;
}

POJ 1084 题目链接

题意

有很多个火柴形成的正方形,有m个火柴已被破坏,问还需要破坏多少个火柴才能破坏所有的正方形

思路

把正方形当成列,火柴当成行,把所有的正方形破坏,就相当于选择所有的列
DLX重复覆盖
预处理所有的火柴和正方形的行列关系,然后标记m个火柴,把这些已经破坏的正方形列去掉
(也可以写个remove1函数(不会写,照抄别人的),在添加所有的结点后,再把m个火柴破坏的列去掉)

void remove1(int r)
    {
        if(H[r]==-1)
            return;

        for(int i=U[H[r]];i!=H[r];i=U[i])
        {
            if(H[row[i]]==i)            // !!!
            {
                if(R[i]==i)
                    H[row[i]]=-1;
                else
                    H[row[i]]=R[i];
            }

            L[R[i]]=L[i];
            R[L[i]]=R[i];
        }

        for(int i=R[H[r]];i!=H[r];i=R[i])
            for(int j=U[i];j!=i;j=U[j])
            {
                if(H[row[j]]==j)
                {
                    if(R[j]==j)
                        H[row[j]]=-1;
                    else
                        H[row[j]]=R[j];
                }

                L[R[j]]=L[j];
                R[L[j]]=R[j];
            }
    }
#include <cstdio>
#include <cmath>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int N = 222;
const int M = 222;
struct node{
    int n,m,id;
    int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
    int H[N];/// 行头结点
    int S[M];
    bool v[N * M];
    int ansd,ans[N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c)
    {
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    void Remove(int c)
    {
        for (int i = D[c]; i != c; i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void Resume(int c)
    {
        for (int i = U[c]; i != c; i = U[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int calc()
    {
        int cnt = 0;
        for (int i = R[0]; i != 0; i = R[i]){
            v[i] = true;
        }
        for (int i = R[0]; i != 0; i = R[i]){
            if (v[i]){
                cnt++;
                v[i] = false;
                for (int j = D[i]; j != i; j = D[j]){
                    for (int k = R[j]; k != j; k = R[k]){
                        v[Col[k]] = false;
                    }
                }
            }
        }
        return cnt;
    }
    void dance(int deep)
    {
        if  (deep + calc() >= ansd) return ;
        if (R[0] == 0) {
            ansd = min(ansd,deep);
            return ;
        }
        int c = R[0];
        for (int i = R[0]; i != 0; i = R[i]){
            if (S[i] < S[c]){
                c = i;
            }
        }
        for (int i = D[c]; i != c; i = D[i]){
            Remove(i);
            for (int j = R[i]; j != i; j = R[j]){
                Remove(j);
            }
            dance(deep + 1);
            for (int j = L[i]; j != i; j = L[j]){
                Resume(j);
            }
            Resume(i);
        }
    }
}DLX;
int id[66][66];
int num1[6] = {0,4,12,24,40,60};
int num2[6] = {0,1,5,14,30,55};
bool vis[66];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d %d",&n,&m);
        int gap = 2 * n + 1;
        int cnt = 0;
        memset(id,0,sizeof(id));
        for (int len = 1; len <= n; len++){     /// 正方形边长
            for (int i = 1; i <= (n - len) * gap + 1; i += gap){   /// 正方形的一层中的第一个火柴编号
                for (int j = i; j - i <= n - len; j++){           /// 遍历正方形的一层
                    ++cnt;
                    for (int k = j; k - j + 1 <= len; k++){        /// 把正方形的上下火柴标记
                        id[k][cnt] = 1;
                        id[k + len * gap][cnt] = 1;
                    //    cout << "cnt = " << cnt << " " << k << " " << k + len * gap << endl;
                    }
                    for (int k = j + n; k <= j + n + gap * (len - 1); k += gap){        /// 把正方形的左右火柴标记
                        id[k][cnt] = 1;
                        id[k + len][cnt] = 1;
                   //     cout << "cnt = " << cnt << " " << k << " " << k + len << endl;
                    }
                }
            }
        }
        int sum = 2 * n * (n + 1);
        DLX.init(sum,cnt);
        memset(vis,true,sizeof(vis));
        for (int i = 0; i < m; i++){
            int x;
            scanf("%d",&x);
            for (int j = 1; j <= num2[n]; j++){
                if (id[x][j] && vis[j]){
                    vis[j] = false;
                    DLX.R[DLX.L[j]] = DLX.R[j];
                    DLX.L[DLX.R[j]] = DLX.L[j];
                    DLX.R[j] = DLX.L[j] = 0;
                }
            }
        }
        for (int i = 1; i <= num1[n]; i++){
            for (int j = 1; j <= num2[n]; j++){
                if (id[i][j] && vis[j]) {
                    DLX.link(i,j);
                }
            }
        }
        DLX.ansd = INF;
        DLX.dance(0);
        printf("%d\n",DLX.ansd);
    }
    return 0;
}

POJ 3074 题目链接

题意

9个3*3的数独

思路

在这里插入图片描述
转化成DLX精准覆盖问题

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1111 * 4;
const int M = 1111;
struct node{
    int n,m,id;
    int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
    int H[N];/// 行头结点
    int S[M];
    int ansd,ans[N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c){
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    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]){
                U[D[j]] = D[U[j]] = j;
                S[Col[j]]++;
            }
        }
        L[R[c]] = R[L[c]] = c;
    }
    bool dance(int deep)
    {
        if (R[0] == 0){
            ansd = deep;
            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[deep] = Row[i];
            for (int j = R[i]; j != i; j = R[j]){
                Remove(Col[j]);
            }
            if (dance(deep + 1)) return true;
            for (int j = L[i]; j != i; j = L[j]){
                Resume(Col[j]);
            }
        }
        Resume(c);
        return false;
    }
}DLX;
char str[99];
int main()
{
    while(scanf("%s",str) != EOF && str[0] != 'e'){
        DLX.init(81 * 9,81 * 4);
        for (int i = 0; i < 9; i++){
            for (int j = 0; j < 9; j++){
                int x = i, y = j,z = i / 3 * 3 + j / 3;
                int id = i * 9 + j;
                if (str[id] == '.'){
                    for (int k = 1; k <= 9; k++){     /// 每一个位置有九种情况(连接对应的数字和行列方格)
                        DLX.link(id * 9 + k,id + 1);             /// 数字
                        DLX.link(id * 9 + k,81 + x * 9 + k);     /// 行
                        DLX.link(id * 9 + k,162 + y * 9 + k);    /// 列
                        DLX.link(id * 9 + k,243 + z * 9 + k);    /// 小方格
                    }
                }
                else {
                    int k = str[id] - '0';
                    DLX.link(id * 9 + k,id + 1);             /// 数字
                    DLX.link(id * 9 + k,81 + x * 9 + k);     /// 行
                    DLX.link(id * 9 + k,162 + y * 9 + k);    /// 列
                    DLX.link(id * 9 + k,243 + z * 9 + k);    /// 小方格
                }
            }
        }
        DLX.dance(0);
        for (int i = 0; i < DLX.ansd; i++){
            int k = DLX.ans[i];
            int a = (k - 1) / 9;  
            int b = (k - 1) % 9 + '1';
            str[a / 9 * 9 + a % 9] = b;
        }
        puts(str);
    }
    return 0;
}

HDU 4069 题目链接

题意

变形版数独,之前的3*3小格变成的连通块
输入的数字为五个数的和,代表那一个块的上下左右是否有墙

思路

先跑一个dfs把每一个块标记出来
注意第一次找到的时候保存结果(因为后面寻找会改变结果)
找到第二个就立刻退出,不然会超时

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10;
const int NM = N * N * N * 4 + N * N * 4 + N;
const int M = N * N * N + N;
struct node{
    int n,m,id;
    int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
    int H[NM];/// 行头结点
    int S[NM];
    int ansd,ans[M],cnt,real[N * N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        cnt = 0;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c){
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    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]){
                U[D[j]] = D[U[j]] = j;
                S[Col[j]]++;
            }
        }
        L[R[c]] = R[L[c]] = c;
    }
    void dance(int deep)
    {
        if (R[0] == 0){
            cnt++;
            if (cnt > 1) return ;
            ansd = deep;
            for (int i = 0; i < deep; i++){
                real[(ans[i] - 1) / 9] = (ans[i] - 1) % 9 + 1;
            }
            return ;
        }
        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[deep] = Row[i];
            for (int j = R[i]; j != i; j = R[j]){
                Remove(Col[j]);
            }
            dance(deep + 1);
            if (cnt > 1) return ;
            for (int j = L[i]; j != i; j = L[j]){
                Resume(Col[j]);
            }
        }
        Resume(c);
    }
}DLX;
int g[N][N];
int id[N][N];
int hinder[N][N][4];
int to[4][2] = {0,-1,1,0,0,1,-1,0};
int num;
void dfs(int x,int y)
{
    id[x][y] = num;
    for (int i = 0; i < 4; i++){
        int xx = x + to[i][0];
        int yy = y + to[i][1];
        if (id[xx][yy] || hinder[x][y][i]) continue;
        dfs(xx,yy);
    }
}
int main()
{

    int t,Case = 1;
    scanf("%d",&t);
    while(t--){
        memset(hinder,0,sizeof(hinder));
        for (int i = 0; i < 9; i++){
            for (int j = 0; j < 9; j++){
                scanf("%d",&g[i][j]);
                if (g[i][j] >= 128) {
                    hinder[i][j][0] = 1;
                    g[i][j] -= 128;
                }
                if (g[i][j] >= 64){
                    hinder[i][j][1] = 1;
                    g[i][j] -= 64;
                }
                if (g[i][j] >= 32) {
                    hinder[i][j][2] = 1;
                    g[i][j] -= 32;
                }
                if (g[i][j] >= 16){
                    hinder[i][j][3] = 1;
                    g[i][j] -= 16;
                }
            }
        }
        num = 1;
        memset(id,0,sizeof(id));
        for (int i = 0; i < 9; i++){
            for (int j = 0; j < 9; j++){
                if (!id[i][j]) {
                    dfs(i,j);
                    num++;
                }
            }
        }
        DLX.init(81 * 9,81 * 4);
        for (int i = 0; i < 9; i++){
            for (int j = 0; j < 9; j++){
                int x = i,y = j,z = id[i][j] - 1;
                int tem = i * 9 + j;
                if (!g[i][j]){
                    for (int k = 1; k <= 9; k++){
                        DLX.link(tem * 9 + k,tem + 1);
                        DLX.link(tem * 9 + k,81 + x * 9 + k);
                        DLX.link(tem * 9 + k,162 + y * 9 + k);
                        DLX.link(tem * 9 + k,243 + z * 9 + k);
                    }
                }
                else {
                    int k = g[i][j];
                    DLX.link(tem * 9 + k,tem + 1);
                    DLX.link(tem * 9 + k,81 + x * 9 + k);
                    DLX.link(tem * 9 + k,162 + y * 9 + k);
                    DLX.link(tem * 9 + k,243 + z * 9 + k);
                }
            }
        }
        DLX.dance(0);
        printf("Case %d:\n",Case++);
        if (DLX.cnt == 0) printf("No solution\n");
        else if (DLX.cnt > 1) printf("Multiple Solutions\n");
        else {
            for (int i = 0; i < DLX.ansd; i++){
                printf("%d",DLX.real[i]);
                if (i % 9 == 8) printf("\n");
            }
        }
    }
    return 0;
}

HDU 3335 题目链接

题意

给出n个数,找出最多的两两不能相除的数

思路

DLX最大可重复覆盖,num[i]与num[j]可以相除就在i行j列和j行i列标1
(我也不知道为什么最大可重复覆盖可以解)
(这道题用二分图匹配也可以解,但是建图的时候是有向图)

for (int i = 0; i < n; i++){
            for (int j = 0; j < i; j++){
                if (a[i] % a[j] == 0 || a[j] % a[i] == 0){
                    g[i][j] = true;
                }
            }
        }
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int NM = 1000010;
const int N = 1111;
const int M = 1111;
struct node{
    int n,m,id;
    int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
    int H[N];/// 行头结点
    int S[N];
    bool v[NM];
    int ansd,ans[N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c)
    {
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    void Remove(int c)
    {
        for (int i = D[c]; i != c; i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void Resume(int c)
    {
        for (int i = U[c]; i != c; i = U[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int calc()
    {
        int cnt = 0;
        for (int i = R[0]; i != 0; i = R[i]){
            v[i] = true;
        }
        for (int i = R[0]; i != 0; i = R[i]){
            if (v[i]){
                cnt++;
                v[i] = false;
                for (int j = D[i]; j != i; j = D[j]){
                    for (int k = R[j]; k != j; k = R[k]){
                        v[Col[k]] = false;
                    }
                }
            }
        }
        return cnt;
    }
    void dance(int deep)
    {
        if (deep + calc() <= ansd) return ;
        if (R[0] == 0) {
            ansd = deep;
            return ;
        }
        int c = R[0];
        for (int i = R[0]; i != 0; i = R[i]){
            if (S[i] < S[c]){
                c = i;
            }
        }
        for (int i = D[c]; i != c; i = D[i]){
            Remove(i);
            for (int j = R[i]; j != i; j = R[j]){
                Remove(j);
            }
            dance(deep + 1);
            for (int j = L[i]; j != i; j = L[j]){
                Resume(j);
            }
            Resume(i);
        }
    }
}DLX;
long long a[N];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        DLX.init(n,n);
        for (int i = 1; i <= n; i++){
            scanf("%I64d",&a[i]);
        }
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= i; j++){
                if ((a[i] % a[j] == 0) || (a[j] % a[i] == 0)){
                    DLX.link(i,j);
                    DLX.link(j,i);
                }
            }
        }
        DLX.ansd = 0;
        DLX.dance(0);
        printf("%d\n",DLX.ansd);
    }
    return 0;
}

HDU 4979 题目链接

题意

n选m彩票中,至少买几张彩票保证至少能中r个数.

思路

使用二进制代表选择的号码
C(n,m)当作行,C(n,r)当作列
要打表,8,5,4这个有点慢

///打表主函数
int main()
{
    int t,Case = 1;
    scanf("%d",&t);
    while(t--){
        int n,m,r;
      //  scanf("%d %d %d",&n,&m,&r);
        for (int i = 1; i <= 8; i++){
            for (int j = 1; j <= i; j++){
                for (int k = 1; k <= j; k++){
                    n = i;
                    m = j;
                    r = k;
                    if (n == 8 && m == 5 && r == 4) continue;
                    int sum1 = fac(n) / fac(m) / fac(n - m);
                    int sum2 = fac(n) / fac(r) / fac(n - r);
                    DLX.init(sum1,sum2);
                    int cnt1 = 0,cnt2 = 0;
                    for (int i = 1; i < pow(2,n); i++){
                        if (calc(i,n) == m) num1[++cnt1] = i;
                        if (calc(i,n) == r) num2[++cnt2] = i;
                    }
                    for (int i = 1; i <= cnt1; i++){
                        for (int j = 1; j <= cnt2; j++){
                            if ((num1[i] & num2[j]) == num2[j]){
                                DLX.link(i,j);
                            }
                        }
                    }
                    DLX.ansd = INF;
                    DLX.dance(0);
                    printf("n = %d m = %d r = %d 总数为:",n,m,r);
                    printf("%d\n",DLX.ansd);
                }
            }
        }
    }
    return 0;
}
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int NM = 1000010;
const int N = 1111;
const int M = 1111;
struct node{
    int n,m,id;
    int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
    int H[N];/// 行头结点
    int S[N];
    bool v[NM];
    int ansd,ans[N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c)
    {
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    void Remove(int c)
    {
        for (int i = D[c]; i != c; i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void Resume(int c)
    {
        for (int i = U[c]; i != c; i = U[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int calc()
    {
        int cnt = 0;
        for (int i = R[0]; i != 0; i = R[i]){
            v[i] = true;
        }
        for (int i = R[0]; i != 0; i = R[i]){
            if (v[i]){
                cnt++;
                v[i] = false;
                for (int j = D[i]; j != i; j = D[j]){
                    for (int k = R[j]; k != j; k = R[k]){
                        v[Col[k]] = false;
                    }
                }
            }
        }
        return cnt;
    }
    void dance(int deep)
    {
        if (deep + calc() >= ansd) return ;
        if (R[0] == 0) {
            ansd = min(ansd,deep);
            return ;
        }
        int c = R[0];
        for (int i = R[0]; i != 0; i = R[i]){
            if (S[i] < S[c]){
                c = i;
            }
        }
        for (int i = D[c]; i != c; i = D[i]){
            Remove(i);
            for (int j = R[i]; j != i; j = R[j]){
                Remove(j);
            }
            dance(deep + 1);
            for (int j = L[i]; j != i; j = L[j]){
                Resume(j);
            }
            Resume(i);
        }
    }
}DLX;
int fac(int n)
{
    int ans = 1;
    for (int i = 1; i <= n; i++){
        ans *= i;
    }
    return ans;
}
int calc(int x,int n)
{
    int ans = 0;
    for (int i = 0; i < n; i++){
        if ((1 << i) & x) ans++;
    }
    return ans;
}
int num1[111];
int num2[111];
int ans[]{
0,8,4,28,3,11,56,2,6,14,70,2,4,8,20,56,2,3,4,7,12,28,
2,3,4,5,6,7,8,1,1,1,1,1,1,1,1
};
int main()
{
    int t,Case = 1;
    scanf("%d",&t);
    while(t--){
        int n,m,r;
        scanf("%d %d %d",&n,&m,&r);
        if (n == 8){
            int num = 1,res;
            bool flag = false;
            for (int i = 1; i <= 8; i++){
                for (int j = 1; j <= i; j++){
                    if (m == i && r == j){
                        res = ans[num];
                        flag = true;
                    }
                    if (flag) break;
                    num++;
                }
            }
            printf("Case #%d: %d\n",Case++,res);
            continue;
        }
        int sum1 = fac(n) / fac(m) / fac(n - m);
        int sum2 = fac(n) / fac(r) / fac(n - r);
        DLX.init(sum1,sum2);
        int cnt1 = 0,cnt2 = 0;
        for (int i = 1; i < pow(2,n); i++){
            if (calc(i,n) == m) num1[++cnt1] = i;
            if (calc(i,n) == r) num2[++cnt2] = i;
        }
        for (int i = 1; i <= cnt1; i++){
            for (int j = 1; j <= cnt2; j++){
                if ((num1[i] & num2[j]) == num2[j]){
                    DLX.link(i,j);
                }
            }
        }
        DLX.ansd = INF;
        DLX.dance(0);
        printf("Case #%d: %d\n",Case++,DLX.ansd);
    }
    return 0;
}

HDU 5046 题目链接

题意

n个城市,建立k个机场,求所有城市到机场的最小距离

思路

二分,类似HDU2295,注意long long

#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int NM = 1000010;
const int N = 1111;
const int M = 1111;
int limit;
struct dlx{
    int n,m,id;
    int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
    int H[N];/// 行头结点
    int S[N];
    bool v[NM];
    int ansd,ans[N];
    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;
        }
        L[0] = m;
        R[m] = 0;
        id = m;
        for (int i = 1; i <= n; i++){
            H[i] = -1;
        }
    }
    void link(int r,int c)
    {
        Col[++id] = c;
        S[c]++;
        Row[id] = r;
        D[id] = D[c];
        U[D[c]] = id;
        U[id] = c;
        D[c] = id;
        if (H[r] < 0){
            H[r] = L[id] = R[id] = id;
        }
        else {
            R[id] = R[H[r]];
            L[R[H[r]]] = id;
            L[id] = H[r];
            R[H[r]] = id;
        }
    }
    void Remove(int c)
    {
        for (int i = D[c]; i != c; i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void Resume(int c)
    {
        for (int i = U[c]; i != c; i = U[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int calc()
    {
        int cnt = 0;
        for (int i = R[0]; i != 0; i = R[i]){
            v[i] = true;
        }
        for (int i = R[0]; i != 0; i = R[i]){
            if (v[i]){
                cnt++;
                v[i] = false;
                for (int j = D[i]; j != i; j = D[j]){
                    for (int k = R[j]; k != j; k = R[k]){
                        v[Col[k]] = false;
                    }
                }
            }
        }
        return cnt;
    }
    bool dance(int deep)
    {
        if (deep + calc() > limit) return false;
        if (R[0] == 0) {
            return deep <= limit;
        }
        int c = R[0];
        for (int i = R[0]; i != 0; i = R[i]){
            if (S[i] < S[c]){
                c = i;
            }
        }
        for (int i = D[c]; i != c; i = D[i]){
            Remove(i);
            for (int j = R[i]; j != i; j = R[j]){
                Remove(j);
            }
            if (dance(deep + 1)) return true;
            for (int j = L[i]; j != i; j = L[j]){
                Resume(j);
            }
            Resume(i);
        }
        return false;
    }
}DLX;
struct node{
    ll x,y;
}s[N];
ll dis(int i,int j)
{
    return (ll)abs(s[i].x - s[j].x) + (ll)abs(s[i].y - s[j].y);
}
int main()
{
    int t,Case = 1;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d %d",&n,&limit);
        for (int i = 1; i <= n; i++){
            scanf("%I64d %I64d",&s[i].x,&s[i].y);
        }
        ll l = 0,r = 100000000000,ans;
        while(l <= r){
            ll mid = (l + r) >> 1;
            DLX.init(n,n);
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= n; j++){
                    if (dis(i,j) <= mid){
                        DLX.link(i,j);
                    }
                }
            }
            if (DLX.dance(0)) {
                ans = mid;
                r = mid - 1;
            }
            else l = mid + 1;
        }
        printf("Case #%d: %I64d\n",Case++,ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值