ZOJ 1031

//2361658     2010-11-30 14:10:56     Accepted     1031     C     490     160     VRS
//1031 拿火柴 除正方形
//贪心+搜索+剪枝+位操作  (其实也是最小二分覆盖问题,不过没用二分匹配去做)
//首先生成火柴跟正方形的对应关系,并用64位保存,由于n<=5,所以火柴最多60根,正方形最多55个
//然后,去除当前不存在的火柴,并通过可去除正方形数(count)对stick进行排序,贪心先拿覆盖最多的火柴;
//然后深搜,同时利用到两种剪枝
//不过看网上说,测试只用到n=4,我这个方法n=5时就很慢。。。- -
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<time.h>

 

#define bool int

typedef struct _STICK
{
    long long squares;
    bool status;           
    int count;               //可除正方形数
}STICK;
STICK stick[65];
STICK stickResult;

//由于把所有正方形保存在long long (64位)中,所以不同段记录不同边长的正方形
long long resFinish[6];
int posBit[6]={0,0,25,41,50,54};

int n;
int minStickNum;
//time_t timeA,timeB;   
int deleteNum;

int cmp(const void *x,const void *y)
{
    STICK *a=(STICK *)x;
    STICK *b=(STICK *)y;
    return b->count-a->count;
}

//对火柴stickNo增加一个可去除的正方形
void BuildBitData(int row,int col,int RorC,int stickNo,int level)
{
    if(row<=0 || row+level>n+1 || col<=0 || col>n-level+1)
        return;
    int temp,loc;
    if(RorC){ temp=row;  row=col;  col=temp; }
    loc=(row-1)*(n-level+1)+col;
    long long tempBit=1;
    tempBit<<=(posBit[level]+loc-1);
    stick[stickNo].squares   |=  tempBit;

    stick[stickNo].count++;
}

//生成火柴与正方形的对应关系
//分两类火柴,横放和竖放,两者是关于对角线对称的,所以只需要计算其一就行
//这里先算出该火柴的row和col,又先假设正方形由二维定义位置,根据推导知道等于:  【j为当前正方形边长】
//      (row-j,col-j+1),(row-j,col-j+2)...(row-j,col);(row,col-j+1),(row,col-j+2)...(row,col)          
//再转换为一维的表示,最后转为位记录
void BuildLink()
{
    int i,j,k,row,col;
    int stickNum;
    bool stick_RorC;
    stickNum=2*n*(n+1);
    for(i=1;i<=stickNum;i++)
    {
        if((i-1)%(2*n+1)<n)                  //注意刚好整除的边界问题,当i刚好等于2n*(n+1)时,模为0
        {
            stick_RorC=0;
            col=i%(2*n+1);
            row=i/(2*n+1)+1;
        }
        else
        {
            stick_RorC=1;
            col=(i-1)/(2*n+1)+1;              //同上注意
            row=(i-1)%(2*n+1)-n+1;
        }
        for(j=1;j<=n;j++)
        {
            for(k=col-j+1;k<=col;k++)
            {
                BuildBitData(row-j,k,stick_RorC,i,j);
                BuildBitData(row,k,stick_RorC,i,j);
            }
        }
    }
}

//先生成最终结果的位值
void BuildResultFinish()
{
    resFinish[1]=(long long)1;
    resFinish[2]=((long long)1<<25)|15;
    resFinish[3]=((long long)1<<41)|((long long)15<<25)|511;
    resFinish[4]=((long long)1<<50)|((long long)15<<41)|((long long)511<<25)|65535;
    resFinish[5]=((long long)1<<54)|((long long)15<<50)|((long long)511<<41)|((long long)65535<<25)|33554431;
}

//可行性剪枝操作
//opt[i] = si & s(i+1) & s(i+2) … & sn 如果剩下的都不能产生结果,就剪掉
bool Option(int k)
{
    long long tempBit=0;
    int i;
    for(i=k;i<2*n*(n+1)-deleteNum;i++)
        tempBit |= stick[i].squares;
    if(resFinish[n]!=(stickResult.squares | tempBit))
        return 1;
    else
        return 0;
}

//回溯,搜索dfs
void Backtrack(int k,int currNum)
{
    STICK stickTemp;
    //用了两种剪枝操作
    //1.当前值大于等于阶段最优解,剪枝
    //2.可行性剪枝
    if(k==2*n*(n+1)-deleteNum || currNum>=minStickNum || Option(k))
        return ;
    else
    {
        stickTemp.squares=stickResult.squares;
        if(stick[k].status==0)
        {
            stickResult.squares |= stick[k].squares;
            if(stickResult.squares==resFinish[n])
            {
                if(currNum+1<minStickNum)
                    minStickNum=currNum+1;
                stickResult.squares=stickTemp.squares;
                return;
            }
            Backtrack(k+1,currNum+1);
            stickResult.squares=stickTemp.squares;
        }
        Backtrack(k+1,currNum);
    }
}

//优化操作1
//如果si与sj的效果一样,去掉火柴j
void DeleteSameData()
{
    int i,j;
    deleteNum=0;
    long long tempBit=0;
    for(i=1;i<=2*n*(n+1);i++)
    {
        tempBit=stick[i].squares | stickResult.squares;
        for(j=i+1;j<=2*n*(n+1);j++)
            if(tempBit==stick[j].squares)
            {
                stick[j].count=-1;
                deleteNum++;
            }
    }
}

int main()
{
    int testcase;
    int inputNum,i,inputData;
    BuildResultFinish();
    scanf("%d",&testcase);
    while(testcase--)
    {
        memset(stick,0,sizeof(stick));
        stickResult.squares=0;
        minStickNum=65;
        scanf("%d\n%d",&n,&inputNum);
        BuildLink();
        for(i=0;i<inputNum;i++)
        {
            scanf("%d",&inputData);
            stickResult.squares |= stick[inputData].squares;
            stick[inputData].status=1;
        }
        DeleteSameData();
//        time(&timeA);

        //优化2,贪心,从可去除最多个正方形的火柴开始取起
        qsort(stick,2*n*(n+1)+1,sizeof(stick[0]),cmp);

        Backtrack(0,inputNum);
       
//        time(&timeB);
        printf("%d\n",minStickNum-inputNum);
//        printf("时间是:%d\n",timeB-timeA);
    }
    return 0;
}

你可能感兴趣的:(ZOJ)