• 18.10.16 队测


    T1 :

    求给定集合有多少个非空子集可以分割成两个集合,使得它们的和相等。

    其中:(nleq20~,~a[i]leq1e8)

    题目可以转化成:给每个数前添加一个系数(pin[-1,1]),使得和为0的方案数。

    由于(n)比较小,所以可以考虑爆搜。朴素的爆搜可以枚举每个数不选,第一个集合,第二个集合,复杂度(O(3^n))

    然后发现根据套路,可以想到折半搜索((meet~ in~ the ~middle))

    先爆搜前十个的状态和(sum),然后爆搜后十个进行匹配,我这里用的是map.

    #pragma GCC optimize(3)
    #include<bits/stdc++.h>
    using std :: vector ;
    using std :: map;
    using std :: clock;
    #define ull unsigned long long
    #define max(a,b) (a<b?b:a)
    #define min(a,b) (a<b?a:b)
    #define swap(x,y) x^=y^=x^=y
    #define isdigit(ch) (ch>='0'&&ch<='9')
    #define abs(x) (x<0?-x:x)
    #define pb push_back
    const int mod = 1e9+7;
    inline void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
    }
    inline void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    inline void write(int x) {print(x);puts("");}
    int n,a[30],sum1,sum2,sum[30],ans;
    namespace task1 {
        int Map[2000000],sta;
        void dfs(int x) {
    	if(x>n) {if(!sum1&&!Map[sta]) Map[sta]=1,ans++;return ;}
    	
    	if(sum[x]+sum1<0) return ;
    	if(-sum[x]+sum1>0) return ;
    	sum1+=a[x];dfs(x+1);sum1-=a[x];
    	sum1-=a[x];dfs(x+1);sum1+=a[x];
    	sta^=1<<(x-1);dfs(x+1);sta^=1<<(x-1);
        }
        void solve() {dfs(1);write(ans-1);}
    }
    namespace task2 {
        struct node {map<int,int > s;vector<int > v;int num;};
        map <int,node > mp;
        map <int,int > mp2[5005];
        int sum=0,ans=0,sta=0,Map[5050];
        void dfs1(int x) {
    	if(x>10) {
    	    if(!mp[sum].s[sta]) mp[sum].s[sta]=1,mp[sum].v.pb(sta),mp[sum].num++;
    	    return ;
    	}
    	sum+=a[x];dfs1(x+1);sum-=a[x];
    	sum-=a[x];dfs1(x+1),sum+=a[x];
    	sta^=1<<(x-1);dfs1(x+1);sta^=1<<(x-1);
        }
        map<int,int > check[5005];
        void dfs(int x) {
    	if(x>n) {
    	    if(check[sta][-sum]) return ;
    	    check[sta][-sum]=1;
    	    int num=mp[-sum].num;
    	    for(int i=0;i<num;i++)
    		if(!mp2[sta][mp[-sum].v[i]]) mp2[sta][mp[-sum].v[i]]=1,ans++;
    	    return ;
    	}
    	sum+=a[x];dfs(x+1);sum-=a[x];
    	sum-=a[x];dfs(x+1),sum+=a[x];
    	sta^=1<<(x-10);dfs(x+1);sta^=1<<(x-10);
        }
        void solve() {
    	dfs1(1);for(int i=0;i<=5000;i++) Map[i]=0;sum=sta=0;
    	dfs(11);write(ans-1);
        }
    }
    int main() {
        // freopen("subsets.in","r",stdin);
        // freopen("subsets.out","w",stdout);
        read(n);for(int i=1;i<=n;i++) read(a[i]);
        for(int i=n;i;i--) sum[i]=sum[i+1]+a[i];
        if(n<=10) task1 :: solve();
        else task2 :: solve();//write(clock());
        return 0;
    }
    
    

    (emacs缩进有点问题...)

    T2:

    给定一个((0,1,2,..,n-1))的排列(p),然后求一个((0,1,2,..,n-1))的排列(q)的个数

    其中,(q)要满足 : 对排列(s=(0,1,2,3,...,n-1))进行(n-1)次交换,每次交换(s[q[i]]),(s[q[i]+1]) 最后要满足(s=p); 答案对(1e9+7)取模

    由于每个位置只能(swap)一次,所以(q)的某些相邻位置的值的大小关系是可以确定的。

    这就是一个简单的(dp)问题,设(f[i][j])位前(i)个数第(i)的大小排名位(j),然后前缀和优化即可。

    #pragma GCC optimize(3)
    #include<cstdio>
    #define isdigit(ch) (ch>='0'&&ch<='9')
    const int mod = 1e9+7;
    inline void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
    }
    inline void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    inline void write(int x) {print(x);puts("");}
    #define maxn 5050
    #define end return puts("0"),0
    int f[maxn][maxn],g[maxn][maxn],p[maxn],tmp[maxn],n;
    int main() {
        read(n);for(int i=0;i<n;i++) read(p[i]);
        for(int i=0;i<n;i++)
    	if(p[i]>i) {
    	    if(i>=1) if(tmp[i-1]==1) end;tmp[i-1]=2;
    	    for(int j=i;j<p[i]-1;j++) if(tmp[j]==2) end;else tmp[j]=1;
    	    if(p[i]<=n-2) if(tmp[p[i]-1]==1) end;else tmp[p[i]-1]=2;
    	}
    	else if(p[i]<i) {
    	    if(p[i]>=1) if(tmp[p[i]-1]==2) end;tmp[p[i]-1]=1;
    	    for(int j=p[i];j<=i-2;j++) if(tmp[j]==1) end;else tmp[j]=2;
    	    if(i<=n-2) if(tmp[i-1]==2) end;else tmp[i-1]=1;
    	} else end;
        f[0][1]=g[0][1]=1;
        for(int i=1;i<n-1;i++)
    	for(int j=1;j<=i+1;j++) {
    	    if(tmp[i-1]!=2) f[i][j]=(f[i][j]+g[i-1][j-1])%mod;
    	    if(tmp[i-1]!=1) f[i][j]=(f[i][j]+g[i-1][i]-g[i-1][j-1])%mod;
    	    g[i][j]=(g[i][j-1]+f[i][j])%mod;
    	}
        int ans=0;
        for(int i=1;i<=n-1;i++) ans=(ans+f[n-2][i])%mod;
        write((ans%mod+mod)%mod);
        return 0;
    }
    
    

    T3 :

    给出一个数组(a) ,有(k)个背包,每个背包能放连续的一段数,然后任取一个数(xin[0,P-1]),给出一个P,对于每个(a[i]),(a[i]=(a[i]+x)\%P),最小化背包存的东西的最大值。

    其中:(Pleq 1e4,nleq1e4)

    sample input :

    5 5 2
    0 4 2 1 3

    sample output :

    5

    hint : x=3,a={3,2,0,4,1},最大值为5.

    首先很容易想到,先暴力枚举(x),然后二分答案,(O(Pn log n)).

    然后(看了题解才知道)可以用随机化优化时间复杂度。

    随找出一个(x),然后(check)一下最优解,如果不能更优,则(continue),否则二分。

    时间复杂度(O(nP+nlognlogP)).

    #pragma GCC optimize(3)
    #include<cstdio>
    #include<ctime>
    #include<algorithm>
    using std :: random_shuffle;
    using std :: clock;
    #define ull unsigned long long
    #define max(a,b) (a<b?b:a)
    #define min(a,b) (a<b?a:b)
    #define swap(x,y) x^=y^=x^=y
    #define isdigit(ch) (ch>='0'&&ch<='9')
    #define abs(x) (x<0?-x:x)
    #define pb push_back
    const int mod = 1e9+7;
    inline void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
    }
    inline void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    inline void write(int x) {print(x);puts("");}
    int n,P,k,a[100050],ans=1e9,g[100040],b[100020];
    int check(int x) {
        int res=0,num=1;
        for(int i=1;i<=n;i++)
    	if(a[i]>x) return false;
     	else if(res+a[i]>x) res=a[i],num++;
     	else res+=a[i];
        return num<=k;
    }
    int main() {
        read(n),read(P),read(k);
        for(int i=1;i<=n;i++) read(b[i]),b[i]%=P;
        for(int i=1;i<=P;i++) 
    	g[i]=i%P;
        random_shuffle(g+1,g+P+1);
        for(int i=1;i<=P;i++) {
    	for(int j=1;j<=n;j++) a[j]=(b[j]+g[i])%P;
    	if(!check(ans)) continue;
    	int l=0,r=ans,res=1e8;
    	while(l<=r) {
    	    int mid=(l+r)>>1;//write(mid);
    	    if(check(mid)) res=mid,r=mid-1;
    	    else l=mid+1;
    	}
    	ans=min(ans,res);
        }write(ans);
        return 0;
    }
    
    
  • 相关阅读:
    firefox打开链接自动跳转至新页面设置
    sql server 查询不为空的字段
    C# 判断ip地址是否正确
    Win7自带功能,刻录光盘遇到的问题
    【一起学源码-微服务】Nexflix Eureka 源码六:在眼花缭乱的代码中,EurekaClient是如何注册的?
    【一起学源码-微服务】Nexflix Eureka 源码五:EurekaClient启动要经历哪些艰难险阻?
    【一起学源码-微服务】Nexflix Eureka 源码四:EurekaServer启动之完成上下文构建及EurekaServer总结
    【一起学源码-微服务】Nexflix Eureka 源码三:EurekaServer启动之EurekaServer上下文EurekaClient创建
    【一起学源码-微服务】Nexflix Eureka 源码二:EurekaServer启动之配置文件加载以及面向接口的配置项读取
    【一起学源码-微服务】Netflix Eureka 源码一:Netflix Eureka 源码初探,我们为什么要读源码?
  • 原文地址:https://www.cnblogs.com/hbyer/p/9799685.html
Copyright © 2020-2023  润新知