zoukankan      html  css  js  c++  java
  • UVA 12035 War Map

    让我先把微笑送给出题人

        这个题最基础的一个想法:先找出一个度数和为总度数和的1/2的点集,然后判断这个点集和这个点集的补集能否形成二分图。但是就算我们把判断的复杂度看成O(1),这个算法的复杂度也是 O(T * 2^n),根本过不了。

        下面我就来列举一下我这两个月做的优化:

        1.把度数序列排序。

    这个看起来没啥用,但是后面好多地方都要用到这个。

        2.meet in the middle。

    我们可以先搜出前一半的所有点集,把它们存在以它们的度数和为下标的链表里;

    然后我们再搜后一半,再从 以 总度数/2 - 搜出的点集度数和 为下标的链表里找点集 ,然后这两个点集加起来就是二分图的左边.

    虽然不知道这个效果咋样,但肯定是比直接搜点集要优很多的。

        3.网络流判断。

    这个其实不能算什么优化,因为判断能不能形成二分图的时候,我实在想不到什么其他办法23333。

    也算是开了一下脑洞把,,,,搜索+网络流也是玄学。。。

        4.询问判重构。

    直接把排完序的度数序列表示成23进制数,这样就可以直接hash(可以用map记,因为这里的log 是并行的不会记到复杂度里)判重构了,如果一组询问以前出现过那么直接输出以前计算出来的答案。

        5.选出的点集判重构。

    这个可不能用map记了,这里如果多个log是要记在复杂度里的,,,很可能就被卡了2333.

    但稍微想一想就能发现,这个是可以用二进制表示的。我们只需要让每种度数对应一个二进制位,并且不产生冲突即可。

    比如说 1,2,2,3,5这个序列吧,我们只需要把它们的对应二进制位设成 2^0,2^1,2^1,2^3,2^4,就可以了。

        6.二分图左右点集的最大度数和点数关系。

    比如说二分图左边的最大度数是6 ,而右边只有5个点的话,是肯定不能形成合法二分图的,所以我们就提前判断而不要跑一遍网络流。

        而且感觉上述6种优化缺一不可,,,因为中间14次UNACCEPT大部分就是因为少了某种优化。。。

    后记:某位同校大佬还想到了一个剪枝,那就是当 总度数/2>最多可能的边数的时候,直接判不可行。

    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    using namespace std;
    vector<int> g[35];
    map<int,int> mmp;
    struct lines{
        int to,flow,cap;
    }l[2333];
    int S,T,t,d[35],q[35],tp,tl,cur[35];
    bool v[35],used[1100005];
     
    inline void add(int from,int to,int cap){
        l[++t]=(lines){to,0,cap},g[from].pb(t);
        l[++t]=(lines){from,0,0},g[to].pb(t);
    }
     
    inline bool BFS(){
        memset(v,0,sizeof(v));
        q[tp=tl=1]=S,v[S]=1,d[S]=0;
         
        int x; lines e;
        while(tp<=tl){
            x=q[tp++];
            for(int i=g[x].size()-1;i>=0;i--){
                e=l[g[x][i]];
                if(e.flow<e.cap&&!v[e.to]){
                    v[e.to]=1,d[e.to]=d[x]+1;
                    q[++tl]=e.to;
                }
            }
        }
         
        return v[T];
    }
     
    int dfs(int x,int A){
        if(x==T||!A) return A;
         
        int flow=0,f,sz=g[x].size();
        for(int &i=cur[x];i<sz;i++){
            lines &e=l[g[x][i]];
            if(d[e.to]==d[x]+1&&(f=dfs(e.to,min(A,e.cap-e.flow)))){
                A-=f,flow+=f;
                e.flow+=f,l[g[x][i]^1].flow-=f;
                if(!A) break;
            }
        }
         
        return flow;
    }
     
    inline int max_flow(){
        int an=0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            an+=dfs(S,1<<30);
        }
        return an;
    }
     
    int G[2333],sum[2333],num,zt[1100005];
    int Q,n,de[25],cnt,ci[33],HD,val[25];
    int hd[445],ne[2333],HF,now,win;
     
    inline bool can(int s){
        int tt=0;
        for(int i=0;i<n;i++) if(ci[i]&s) tt+=ci[val[i]];
        if(used[tt]) return 0;
        used[tt]=1,zt[++num]=tt;
     
        int OT=0,OM=0,ZT=0,ZM=0;
        for(int i=0;i<n;i++)
            if(ci[i]&s) OT++,OM=max(OM,de[i]);
            else ZT++,ZM=max(ZM,de[i]);
        if(OT<ZM||ZT<OM) return 0;
     
        S=0,T=n+1,t=-1;
        for(int i=0;i<=T;i++) g[i].clear();
        for(int i=0;i<n;i++)
            if(ci[i]&s) add(S,i+1,de[i]);
            else add(i+1,T,de[i]);
        for(int i=0;i<n;i++) if(ci[i]&s)
            for(int j=0;j<n;j++) if(!(ci[j]&s)) add(i+1,j+1,1);
         
        return max_flow()==HD;
    }
     
    inline void front_search(){
        for(int i=0;i<HF;i++) sum[ci[i]]=de[i];
        for(int i=0,N,L;i<ci[HF];i++){
            if(i){
                N=i&-i,L=N^i;
                sum[i]=sum[N]+sum[L];
            }
             
            G[++cnt]=i,ne[cnt]=hd[sum[i]],hd[sum[i]]=cnt;
        }
    }
     
    inline void second_search(){
        for(int i=0;i<n-HF;i++) sum[ci[i]]=de[HF+i];
        for(int i=0,N,L;i<ci[n-HF];i++){
            if(i){
                N=i&-i,L=N^i;
                sum[i]=sum[N]+sum[L];
            }
             
            if(HD-sum[i]>=0){
                N=HD-sum[i];
                int x;
                for(int j=hd[N];j;j=ne[j]){
                    x=G[j];
                    if(can(x+i*ci[HF])){
                        win=1;
                        return;
                    }
                }
            }
        }
    }
     
    inline void solve(int CA){
        for(int i=1;i<=num;i++) used[zt[i]]=0;
        num=cnt=now=win=0,memset(hd,0,sizeof(hd));
        scanf("%d",&n),HF=n>>1,HD=0;
        for(int i=0;i<n;i++) scanf("%d",de+i),HD+=de[i];
        sort(de,de+n),printf("Case %d: ",CA);
         
        val[0]=0;
        for(int i=1;i<n;i++)
            if(de[i]==de[i-1]) val[i]=val[i-1];
            else val[i]=i;
        for(int i=0;i<n;i++) now=now*23+de[i];
         
        if(mmp.count(now)){
            if(mmp[now]==1) puts("YES");
            else puts("NO");
            return;
        }
         
        if(HD&1){
            puts("NO");
            return;
        }
        HD>>=1;
        if(HD>HF*(n-HF)){
        	puts("NO");
        	return;
        }
         
        front_search();
        second_search();
         
        mmp[now]=win;
        if(win) puts("YES");
        else puts("NO");
    }
     
    int main(){
    //  freopen("warmap.in","r",stdin);
    //  freopen("warmap.out","w",stdout);
         
        ci[0]=1;
        for(int i=1;i<=20;i++) ci[i]=ci[i-1]<<1;
        scanf("%d",&Q);
        for(int i=1;i<=Q;i++) solve(i);
        return 0;
    }
    

      

      

  • 相关阅读:
    Luogu5860 「SWTR-03」Counting Trees
    $NOIP1998$ 题解报告
    $NOIP1997$ 题解报告
    $NOIP2008$ 题解报告
    $NOIP2004$ 题解报告
    $NOIP2010$ 题解报告
    $NOIP2009$ 题解报告
    Luogu P1879 玉米田 题解报告
    CH301 任务安排2 题解报告
    $[NOIp2017]$ 逛公园 $dp$/记搜
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8685976.html
Copyright © 2011-2022 走看看