• 【模拟赛】纪中提高A组 19.8.1 测试


    第一天,题目难度适中易改,出现了以前模拟赛做过的,但却没有做出来。

    T1.水叮当的舞步

    题目数据范围较小,考虑搜索。在每一步枚举当前选择的颜色。

    此时时间复杂度为 (O(ans^6))(ans) 最坏情况下为 (N imes N),若将搜索树整颗遍历完显然时间 ((N^{12})) 是不能接受的。

    因此:

    1. 考虑限制搜索深度以提高搜索效率 (IDA*) 。

    2. 考虑某个状态下,达到目标状态还需要的步数最小可能是剩下的颜色种类,作为一个剪枝。

    IDA* + 剪枝 即可通过本题。

    代码:

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    template<class T>void read(T &x){
    	x=0; bool f=0; char c=getchar();
    	while(c<'0'||'9'<c){f|=(c=='-'); c=getchar();}
    	while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
    	x=f?-x:x;
    }
    
    const int N=10;
    
    int n,lim;
    int a[N][N],cnt,t[N];
    struct node{
    	int x,y;
    }p[N*N];
    bool in[N][N];
    int dx[5]={0,1,0,-1};
    int dy[5]={1,0,-1,0};
    
    void init() {
    	memset(a,0,sizeof(a));
    	memset(t,0,sizeof(t));
    	memset(p,0,sizeof(p));
    	memset(in,0,sizeof(in));
    	lim=cnt=0;
    }
    void ins(int c) {
    	for(int i=1;i<=cnt;i++)
    		for(int j=0;j<=3;j++) {
    			int nx=p[i].x+dx[j], ny=p[i].y+dy[j];
    			if(nx<1||ny<1||nx>n||ny>n) continue;
    			if(a[nx][ny]==c) if(!in[nx][ny]) {
    				--t[a[nx][ny]];
    				p[++cnt]=(node){nx,ny};
    				in[nx][ny]=1;
    			}
    		}
    }
    void del(int rem) {
    	while(cnt>rem){
    		++t[a[p[cnt].x][p[cnt].y]];
    		in[p[cnt].x][p[cnt].y]=0;
    		p[cnt--]=(node){0,0};
    	}
    }
    bool dfs(int dep){
    //	if(dep>lim) return 0;
    	int tmp=0;
    	for(int i=0;i<=5;i++) if(t[i]) ++tmp;
    	if(dep+tmp>lim) return 0;
    	if(cnt==n*n) return 1;
    	for(int i=0;i<=5;i++) {
    		tmp=cnt; ins(i);
    		if(tmp!=cnt) {
    			bool suc=dfs(dep+1);
    			del(tmp);
    			if(suc) return 1;
    		}
    	}
    	return 0;
    }
    		
    void solve() {
    	init();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++) {
    			read(a[i][j]);
    			++t[a[i][j]];
    		}
    	in[1][1]=1;
    	p[++cnt]=(node){1,1};
    	--t[a[1][1]];
    	ins(a[1][1]);
    	while(lim<=n*n) {
    		if(dfs(0)) {
    			printf("%d
    ",lim);
    			return ;
    		}
    		++lim;
    	}
    }
    
    int main() {
    //	freopen("a.in","r",stdin); 
    //	freopen("a.out","w",stdout);
    	read(n);
    	while(n) {
    		solve();
    		read(n);
    	}
    	return 0;
    }
    

    T2.Vani和Cl2捉迷藏

    题意是求一张有向图的最大反链。反链即一个点集,其中任意两点间不能到达。

    在图上跑传递闭包,图转化为一张二分图,匈牙利算法求最大独立集即可通过本题。(原题 (CTSC2008 river)

    代码:

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    template<class T>void read(T &x){
    	x=0; char c=getchar();
    	while(c<'0'||'9'<c)c=getchar();
    	while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
    }
    
    const int N=205;
    
    int n,m,ans;
    int match[N];
    bool f[N][N],vis[N];
    
    bool dfs(int x){
    	for(int y=1;y<=n;y++) if(f[x][y]) if(!vis[y]) {
    		vis[y]=1;
    		if(!match[y]||dfs(match[y])){
    			match[y]=x;
    			return 1;
    		}
    	}
    	return 0;
    }
    
    int main(){
    	read(n); read(m);
    	int x,y;
    	for(int i=1;i<=m;i++){
    		read(x); read(y);
    		f[x][y]=1;
    	}
    	for(int k=1;k<=n;k++)
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    				f[i][j]|=f[i][k]&&f[k][j];
    	for(int i=1;i<=n;i++){
    		memset(vis,0,sizeof(vis));
    		if(dfs(i)) ++ans;
    	}
    	printf("%d
    ",n-ans);
    	return 0;
    }
    

    T3.粉刷匠

    最朴素的想法是 (f_{i,r1,r2,...,rk}) 表示粉刷到第 (i) 根石柱,(k) 种颜色分别剩下 (r_i) 桶的情况。时间和空间上都不能接受。

    上面的做法可以利用 (c) 的取值范围来优化,把 k 种颜色剩下的桶数按剩下的数量分组,可以通过本题,代码待写。

    换种思路,不考虑逐个计算每一根柱子,考虑把上好色的柱子放进处理好的柱子里。

    使用另一种状态的定义方式 (f_{i,j}) 表示当前处理完了前i种颜色,用j对柱子颜色相同。

    每次把第 (i) 种颜色插进前面的柱子里,只有两种情况:插到 (2) 根同色的柱子间,插到 (2) 根异色的柱子间。

    所以第 (i) 种颜色插进去会减少 (j) 对同色柱子的颜色,同时产生一些同色柱子。

    我们决定把 (c)(i) 色柱子拆成 (x) 块,把其中 (y) 块放进同色柱子间,其他放进异色柱子间。

    不管怎么分,(c) 根柱子分成 (x) 块都必然有 (c-x) 对同色,(j) 增加了 (c-x)。方案数 (C^{x-1}_{c-1})

    因为 (y) 块放进了已经存在的 (j) 对同色柱子间,(j) 减少 (y)。方案数 (C^{y-1}_{j-1})

    剩下的 (x-y) 块柱子就放进 (n-j+1) 对异色柱子间。方案数 (C^{n-j+1}_{x-y})

    代码:

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    template<class T>void read(T &x){
    	x=0; bool f=0; char c=getchar();
    	while(c<'0'||'9'<c){f|=(c=='-'); c=getchar();}
    	while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
    	x=f?-x:x;
    }
    typedef long long ll;
    const int N=20;
    const int M=1000000007;
    int k,n,a[N];
    ll f[N][N*N],c[N*N][N*N];
    void add(ll &x,ll y){x+=y; if(x>=M)x%=M;}
    void pre(){
    	c[0][0]=1;
    	for(int i=1;i<=100;i++){
    		c[i][0]=c[i][i]=1;
    		for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1];
    	}
    }
    
    void solve(){
    	f[0][0]=1;
    	for(int i=1;i<=k;i++){
    		for(int j=0;j<=n;j++)
    			for(int x=0;x<=a[i];x++){
    				int in=min(x,j);
    				for(int y=0;y<=in;y++)
    					add(f[i][j+a[i]-x-y],f[i-1][j]*c[j][y]%M*c[a[i]-1][x-1]%M*c[n-j+1][x-y]);
    				}
    		n+=a[i];
    	}
    	printf("%lld
    ",f[k][0]);
    }
    
    int main() {
    //	freopen("c.in","r",stdin);
    	pre();
    	int T; read(T);
    	while(T--) {
    		read(k); n=0;
    		memset(a,0,sizeof(a));
    		memset(f,0,sizeof(f));
    		for(int i=1;i<=k;i++) read(a[i]);
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    mysql将一个表的数据 重复复制多份到表中
    PHP中将指定文本内容导入到word中
    系统安全-SElinux
    通过身份证号码提取年龄,性别
    MySQL-获取某天的数据
    mysql-介绍、MySQL部署、数据类型、存储引擎
    监控系统-ELK
    监控系统-Grafana
    监控系统-zabbix
    监控系统-openfalcon
  • 原文地址:https://www.cnblogs.com/opethrax/p/11362873.html
Copyright © 2020-2023  润新知