zoj 3209 Treasure Map 跳舞链好题

原题连接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3209

这个题我想到了用跳舞链,但是我想跳舞链的原理就是搜索,能有多高的效率呢?这个题的数据规模是有500 *30 * 30的,我不知道跳舞链的时间复杂度,随意我犹豫了,但是还是用跳舞链敲了一边,没想到1A,重要的是,以前我一次也没有a掉过一个跳舞链,虽然有模板


解题思路:跳舞链解决的问题是,选择若干的行使得每一列有且仅有一个1,这个题是,选择若干的矩形使得每一个坐标有且仅有一个矩形覆盖

本人还有个疑惑,500*15000的矩阵用跳舞链居然能a了,我想知道,跳舞链到底能解决多大的矩阵?也就是时间复杂度是多少?


具体的看代码吧,我调了好久才对呢


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
#define MAX 0x3f3f3f3f

#define SIZE 100005
bool arr[510][SIZE];
int L[SIZE], R[SIZE], U[SIZE], D[SIZE],Col[SIZE], Row[SIZE]; //这个地方我没有好好的分析每一个数组的具体长度是多少
//所在列 所在行
int S[SIZE];
//列元素数
int sel[SIZE],seln;
//选择的行

int mi;
struct Dancer  {
       void init(int height,int width)
       {
              int p/*节点编号*/, x, y, last;
              for (p = 1; p <= width; p++)
                     //初始化列头指针
              {
                     L[p] = p - 1; R[p] = p + 1;
                     //左右相邻
                     U[p] = D[p] = p; //只有一列
                     S[p] = 0; //列元素数
                     //
              }
              p = width + 1;
              for (y = 1; y <= height; y++) //行
              {
                     last = R[0] = L[0] = 0;//暂作为每行虚拟头指针
                     for (x = 1; x <= width; x++) //列
                     {
                            if (arr[y-1][x-1] != 0)  // 0~n-1 -> 1~n !!!!!!!!!!!!!!!
                            {
                                   //添到行链表尾部
                                   L[p] = last; R[last] = p;
                                   //添到列链表尾部
                                   U[p] = U[x]; D[p] = x; U[x] = D[U[x]] = p;
                                   记录所在行列
                                   Row[p]=y; Col[p]=x;
                                   S[x]++; last=p++;
                            }
                     }

                     // 去虚头,构成行循环链表
                     R[last] = R[0]; L[R[0]] = last;
              }
              R[width] = 0;
              L[0] = width; R[0] = 1;
              S[0] = MAX;
       }
       //删除c列上所有1元素所在的行
       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[Col[j]];
                     }
       }

       //恢复c列上所有1元素所在的行
       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])
                     {
                            U[D[j]] = j;
                            D[U[j]] = j;
                            ++S[Col[j]];
                     }
                     L[R[c]] = c; R[L[c]] = c;//经典恢复
       }

    bool dance()
       {
              if (R[0] == 0)
                     return true;//没有列了
              int c=0, i, j;
              for (i = R[0]; i != 0; i = R[i])//找元素最少的列c
        if (S[i] < S[c]) c = i;
        remove(c);
        for (i = D[c]; i != c; i = D[i])//删除与c相连的行i
        {
            for (j = R[i]; j != i; j = R[j])//删除行元素所在的列j

                remove(Col[j]);
            sel[seln]=Row[i];//选择此行 保存行号
            seln++;
            if (dance())//对于不同的题,这个地方常常需要改动
            {
                mi = min(mi,seln);
            }
            seln--;
            for (j = L[i]; j != i; j = L[j])
                resume(Col[j]);
        }
        resume(c);
        return false;
       }
} dc;

int main()
{
    int n,m,p;
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n >> m >> p;
        memset(arr,0,sizeof(arr));
        for(int k = 0;k < p;k++)
        {
            int x1,x2,y1,y2;
            cin >> x1 >> y1 >> x2 >> y2;
            for(int i = x1;i < x2;i++)
            {
                for(int j = y1;j < y2;j++)
                {
                    arr[k][i*m+j] = 1;
                }
            }
        }
        ///
        seln=0;

        mi = 100000;
        dc.init(p,n*m);
        dc.dance();
        if(mi < 100000)
        {
            cout << mi << "\n";
        }
        else
            cout << "-1";
        putchar('\n');
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值