zoukankan      html  css  js  c++  java
  • fzu 1686(DLX 重复点覆盖)

    可以重复的点覆盖, 写法和一般的DLX 很相似,但是在找到选择一行的时候,只删去这一行上所有点的列。   还要注意的是启发函数起了很大的剪枝作用。

    Problem 1686 神龙的难题

    Accept: 331    Submit: 1111 Time Limit: 1000 mSec    Memory Limit : 32768 KB

    Problem Description

    这是个剑与魔法的世界.英雄和魔物同在,动荡和安定并存.但总的来说,库尔特王国是个安宁的国家,人民安居乐业,魔物也比较少.但是.总有一些魔物不时会进入城市附近,干扰人民的生活.就要有一些人出来守护居民们不被魔物侵害.魔法使艾米莉就是这样的一个人.她骑着她的坐骑,神龙米格拉一起消灭干扰人类生存的魔物,维护王国的安定.艾米莉希望能够在损伤最小的前提下完成任务.每次战斗前,她都用时间停止魔法停住时间,然后米格拉他就可以发出火球烧死敌人.米格拉想知道,他如何以最快的速度消灭敌人,减轻艾米莉的负担.

    Input

    数据有多组,你要处理到EOF为止.每组数据第一行有两个数,n,m,(1<=n,m<=15)表示这次任务的地区范围. 然后接下来有n行,每行m个整数,如为1表示该点有怪物,为0表示该点无怪物.然后接下一行有两个整数,n1,m1 (n1<=n,m1<=m)分别表示米格拉一次能攻击的行,列数(行列不能互换),假设米格拉一单位时间能发出一个火球,所有怪物都可一击必杀.

    Output

    输出一行,一个整数,表示米格拉消灭所有魔物的最短时间.

    Sample Input

    4 4 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1 2 2 4 4 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 2 2

    Sample Output

    4 1

    Source

    FOJ月赛-2009年2月- TimeLoop
     
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    using namespace std;
    #define N 1000000
    #define INF 0x3ffffff
    int U[N],D[N],R[N],L[N],num[N],col[N],line[N],H[N];
    int g[20][20],tg[250][250];;
    int n,m;
    int tn,tm;
    int nn,mm;
    int head;
    int id;
    int mi;
    
    
    void prepare()
    {
        for(int i=0;i<=mm;i++)
        {
            num[i]=0;
            U[i]=i;
            D[i]=i;
            R[i]=i+1;
            L[i+1]=i;
        }
        R[mm]=0;
        L[0]=mm;
        memset(H,-1,sizeof(H)); // 列的头指针
    }
    
    void link(int n1,int m1)
    {
        id++;
        ++num[ line[id]=m1 ];
        col[id]=n1;
        
        D[id]=D[m1];
        U[ D[m1]]=id;
        U[id]=m1;
        D[m1]=id;
        
        if(H[n1]<0) H[n1]=L[id]=R[id]=id;
        else
        {
            L[ R[ H[n1] ] ]=id;
            R[id]=R[H[n1]];
            L[id]=H[n1];
            R[ H[n1] ]=id;
        }
    }
    
    void build()
    {
        id=mm;
        prepare();
        for(int i=0;i<=n-tn;i++)
        {
            for(int j=0;j<=m-tm;j++)
            {
                int flag=0;
                for(int i1=1;i1<=tn;i1++)
                    for(int j1=1;j1<=tm;j1++)
                    {
                        if(g[i+i1][j+j1]!=0)
                        {
                            flag=1;
                            link(nn,g[i+i1][j+j1]);
                        }
                    }
                    nn++;
            }
        }
    }
    
    void remove(int s)
    {
        for(int i=D[s];i!=s;i=D[i])
        {
            L[R[i]]=L[i];
            R[L[i]]=R[i];
        }
    }
    
    void resume(int s)
    {
        for(int i=D[s];i!=s;i=D[i])
        {
            L[ R[i] ]=i;
            R[ L[i] ]=i;
        }
    }
    
    /*int h() // 求出填满所有列所需的最小行
    {
        int mark[N];
        memset(mark,0,sizeof(mark));
        
    }*/
    
    void print()
    {
        
        memset(tg,0,sizeof(tg));
        for(int i=R[head];i!=head;i=R[i])
        {
            for(int j=D[i];j!=i;j=D[j])
                tg[col[j]][line[j]]=1;
        }
        for(int i=1;i<nn;i++)
        {
            for(int j=1;j<=mm;j++)
                printf("%d ",tg[i][j]);
            printf("\n");
        }
    }
    int h()
    {
        int mark[250];
        memset(mark,0,sizeof(mark));
        int sum=0;
        for(int i=R[head];i!=head;i=R[i])
        {
            if(mark[i]==0)
            {
                sum++;
                mark[i]=1;
                for(int j=D[i];j!=i;j=D[j])
                    for(int k=R[j];k!=j;k=R[k])
                        mark[line[k]]=1;
            }
        }
        return sum;
    }
    void dfs(int s)
    {
        if(s+h()>=mi) return ;
        if(R[head]==head)
        {
            mi=s;
            return ;
        }
        int tmi=INF,tu;
        for(int i=R[head];i!=head;i=R[i])
        {
            if(num[i]<tmi)
            {
                tmi=num[i];
                tu=i;
            }
        }
        
        for(int i=D[tu];i!=tu;i=D[i])
        {
            remove(i);
            for(int j=R[i];j!=i;j=R[j])
                remove(j);
            dfs(s+1);
            for(int j=L[i];j!=i;j=L[j])
                resume(j);
            resume(i);
        }
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            head=0;
            nn=1;
            mm=0;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    scanf("%d",&g[i][j]);
                    if(g[i][j]==1)
                    {
                        mm++;
                        g[i][j]=mm;
                    }
                }
            scanf("%d%d",&tn,&tm);
            build();
            mi=INF;
            //print();
            dfs(0);
            
            printf("%d\n",mi);
        }
        return 0;
    }
  • 相关阅读:
    序列
    笔算开方法
    笔算开方法
    【AFO】闷声发大财
    P1092 虫食算[搜索]
    数据结构总结
    P1486 [NOI2004]郁闷的出纳员[权值线段树]
    P1850 换教室[dp+期望]
    P4281 [AHOI2008]紧急集合 / 聚会[LCA]
    P5021 赛道修建[贪心+二分]
  • 原文地址:https://www.cnblogs.com/chenhuan001/p/3007292.html
Copyright © 2011-2022 走看看