ssoj 2279 磁力阵

说不想改最后还是向T1屈服了。。然后就de了一下午Bug。。。

虽然昨天随口扯的有点道理,正解就是迭代加深A星搜索,但实际写起来就十分难受了。

说自己的做法,略鬼畜。

每个正方形的边界上的边、每条边在哪些正方形上,都可以用一个Long Long的二进制串表示。给每个矩形编号,预处理每个矩形对应边的串,每条边对应矩形的串,每个矩形对应矩形(它的所有边对应矩形的并集,之后估价会用)。

然后就直接迭代搜索,存一个串表示现在哪些正方形处理完了,每次找出第一个没处理的正方形,枚举每条边试图处理它。用一个估价函数判断现在有没有必要继续搜:找当前状态所有未处理的正方形,把它的所有边处理掉,遇到一个就res++,若res+cnt>limit 就return 0;

几个坑点,可能只有自己会被坑。。。

1.不用二进制优化会TLE,可能是我写得比较残,T2个点。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
typedef long long LL;
using namespace std;
int T,n,totn,k,x,xx,tot,now,nowok,e[100][100],sq[100][100],ok[100],tpok[100],limit;
void clear() {
    memset(e,0,sizeof(e));
    memset(sq,0,sizeof(sq));
    memset(ok,0,sizeof(ok));
    tot=0; nowok=0;
}
void link(int ed,int id) {
    e[ed][++e[ed][0]]=id;
    sq[id][++sq[id][0]]=ed;
}
void pre() {
    totn=0;
    for(int i=1;i<=n;i++) totn+=i*i;
    for(int k=0;k<=n;k++)
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++)
            if(i+k<=n&&j+k<=n)
             {
                tot++;
                for(int l=0;l<=k;l++) {
                    now=(i-1)*(2*n+1)+j+l;
                    link(now,tot);
                    now=(i+k)*(2*n+1)+j+l;
                    link(now,tot);
                }
                now=n+(i-1)*(2*n+1)+j; 
                link(now,tot); 
                link(now+k+1,tot);
                for(int l=1;l<=k;l++) {
                    now+=(2*n+1);
                    link(now,tot);
                    link(now+k+1,tot);
                }
            }
}
int check(int x,int c,int li) {
    memset(tpok,0,sizeof(tpok));
    int res=0;
    for(int i=1;i<=totn;i++)
    if(!ok[i]&&!tpok[i]) {
         res++;
         for(int j=1;j<=sq[i][0];j++) {
             int u=sq[i][j];
             for(int l=1;l<=e[u][0];l++) 
                 tpok[e[u][l]]=1;
         }
    }
    return res;
}
int dfs(int cnt,int no,int lim) {
    if(cnt>lim) return 0;
    if(!no) return 1;
    int tp[100],flag=0;
    memset(tp,0,sizeof(tp));
    for(int i=1;i<=totn;i++) if(flag) break; else {
       if(!ok[i]){
           flag=1;
        for(int j=1;j<=sq[i][0];j++) {
            xx=0;   tp[0]=0;
            x=sq[i][j];
            for(int l=1;l<=e[x][0];l++) {
               if(!ok[e[x][l]]) {tp[++tp[0]]=e[x][l]; xx++;}
               ok[e[x][l]]=1;
            }   
            if(check(sq[i][j],cnt,lim)+cnt<=lim) {
                if(dfs(cnt+1,no-xx,lim)) return 1;
            }
            for(int i=1;i<=tp[0];i++)
                ok[tp[i]]=0;
        }
       }
    }
    return 0;
}
int main()
{
    freopen("mag.in","r",stdin);
    freopen("mag.out","w",stdout);
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&k);
        clear();
        pre();
        for(int i=1;i<=k;i++) {
            scanf("%d",&x);
            for(int j=1;j<=e[x][0];j++) {
                if(!ok[e[x][j]]) nowok++;
                ok[e[x][j]]=1;
            }
        }
        for(limit=0;limit<=60;limit++) {
            if(dfs(0,totn-nowok,limit)) {
                printf("%d\n",limit);
                break;
            }
        }
    }
    return 0;
}
View Code

2.搜索的时候只需要搜当前状态第一个不满足的正方形,因为其他在之后的状态中是可以搜到的,不然会T8个点。。。

3.正方形的编号,因为搜索是按编号搜的,要先编小正方形再编大的。。自行体会。。会T两个点,速度是0.2秒和10.2秒的差距。。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
typedef long long LL;
using namespace std;
int T,n,totn,k,x,xx,now,tot,nowok,sqq[100][100];
LL e[100],sq[100],val[100],st,limit,N;
void clear() {
    memset(e,0,sizeof(e));
    memset(sq,0,sizeof(sq));
    memset(sqq,0,sizeof(sqq));
    memset(val,0,sizeof(val));
    tot=0; st=0;
}
void link(int ed,int id) {
    e[ed]|=1LL<<(id-1);
    sq[id]|=1LL<<(ed-1);
    sqq[id][++sqq[id][0]]=ed;
}
void pre() {
    totn=0;
    for(int i=1;i<=n;i++) totn+=i*i;
    N=(1LL<<totn)-1;
    for(int k=0;k<=n;k++)
        for(int i=1;i<=n;i++) 
            for(int j=1;j<=n;j++) 
            if(i+k<=n&&j+k<=n)
             {
                tot++;
                for(int l=0;l<=k;l++) {
                    now=(i-1)*(2*n+1)+j+l;
                    link(now,tot);
                    now=(i+k)*(2*n+1)+j+l;
                    link(now,tot);
                }
                now=n+(i-1)*(2*n+1)+j; 
                link(now,tot); 
                link(now+k+1,tot);
                for(int l=1;l<=k;l++) {
                    now+=(2*n+1);
                    link(now,tot);
                    link(now+k+1,tot);
                }
            }
    for(int i=1;i<=totn;i++) {
        for(int j=1;j<=sqq[i][0];j++) {
            val[i]|=e[sqq[i][j]];
        }
    }
}
int check(LL now,int c,int li) {
    int res=0;
    for(int i=1;i<=totn-1;i++) {
        if(!(now&(1LL<<i-1))) {
            res++;
            if(res+c>li) return 0;
            now|=val[i];
        }
    }
    return res+c<=li;
}
int dfs(int cnt,LL now,int lim) {
    if(cnt>lim) return 0;
    if(now==N) return 1;
    LL tmp;
    if(!check(now,cnt,lim)) return 0;
    int flag=0;
    for(int i=1;i<=totn;i++) if(flag) break; else{
       if(!(now&(1LL<<i-1))){
               flag=1; 
            
               for(int j=1;j<=sqq[i][0];j++) {
                   tmp=now|e[sqq[i][j]];
                //if(check(tmp,cnt,lim)) {
                if(dfs(cnt+1,tmp,lim)) return 1;
                //}  
               }
       }
    }
    return 0;
}
int main()
{
    freopen("mag.in","r",stdin);
    freopen("mag.out","w",stdout);
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&k);
        clear();
        pre();
        for(int i=1;i<=k;i++) {
            scanf("%d",&x);
            st|=e[x];
        }
        for(limit=0;limit<=60;limit++) {
            if(dfs(0,st,limit)) {
                printf("%d\n",limit);
                break;
            }
        }
    }
    return 0;
}
AC

 

posted @ 2017-09-16 16:54  啊宸  阅读(265)  评论(2编辑  收藏  举报