• 17.10.30


      • 上午
        • 模拟考试,题太简单,老师连网都没断、、、
          • Prob.1(AC)BFS,裸裸裸!
          • Prob.2(AC)dp,刷表法比较方便
          • Prob.3(RE2个点)一个费用流,要拆点。结果数组就开小了。某兔给spfa加了一个优先队列想要“优化”,结果还超时了两组。这东西有毒不能随便用啊。以后要优化的话,就最好用deque吧。
        • BOZJ 1084 [SCOI2005]最大子矩阵

    m==1和m==2两种情况分开做。
    m==1 就不说了
    m==2:
        dp[i][j][l]表示第一列选到了i,第二列选到了j,且共选了l个子矩阵的最大值
        三种转移:
            1).枚举i向上的连续矩阵
            2).枚举j向上的连续矩阵
            3).如果i==j 枚举i,j以前向上的连续矩阵

    代码:

    #include<cstring>
    #include<iostream>
    using namespace std;
    int sum[105][3];
    int n,m,x;
    void cmax(int &a,int b){
        if(a<b) a=b;
    }
    void solve1(){
        int dp[105][15];
        memset(dp,0xc0,sizeof(dp));
        dp[0][0]=0;
        for(int i=1;i<=n;i++)
            for(int l=0;l<=x;l++){
                dp[i][l]=dp[i-1][l];
                if(l==0) continue;
                for(int k=i;k>=1;k--)
                    cmax(dp[i][l],dp[k-1][l-1]+sum[i][1]-sum[k-1][1]);
            }
        printf("%d",dp[n][x]);
    }
    void solve2(){
        int dp[105][105][15];
        memset(dp,0xc0,sizeof(dp));
        dp[0][0][0]=0;
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++) if(i||j)
                for(int l=0;l<=x;l++){
                    dp[i][j][l]=max((i-1>=0?dp[i-1][j][l]:(int)0xc0c0c0c0),(j-1>=0?dp[i][j-1][l]:(int)0xc0c0c0c0));
                    if(l==0) continue;
                    for(int k=i;k>=1;k--)
                        cmax(dp[i][j][l],dp[k-1][j][l-1]+sum[i][1]-sum[k-1][1]);
                    for(int k=j;k>=1;k--)
                        cmax(dp[i][j][l],dp[i][k-1][l-1]+sum[j][2]-sum[k-1][2]);
                    if(i!=j) continue;
                    for(int k=i;k>=1;k--)
                        cmax(dp[i][j][l],dp[k-1][k-1][l-1]+sum[i][1]-sum[k-1][1]+sum[j][2]-sum[k-1][2]);
                }
        printf("%d",dp[n][n][x]);
    }
    int main(){
        scanf("%d%d%d",&n,&m,&x);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){ 
                scanf("%d",&sum[i][j]);
                sum[i][j]+=sum[i-1][j];
            }
        if(m==1) solve1();
        else solve2();
        return 0;
    }
      • 下午
        • BOZJ 1085 [SCOI2005]骑士精神

    IDA*
    如果当期的棋盘与目标棋盘的差异大于剩下的操作数+1,就return 0;

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int mv[8][2]={{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};
    int aim[6][6]={{0,0,0,0,0,0},{0,1,1,1,1,1},{0,0,1,1,1,1},{0,0,0,2,1,1},{0,0,0,0,0,1},{0,0,0,0,0,0}};
    int now[6][6],sx,sy;
    bool inmap(int x,int y){
    	return 1<=x&&x<=5&&1<=y&&y<=5;
    }
    int differ(){
    	int cnt=0;
    	for(int i=1;i<=5;i++)
    		for(int j=1;j<=5;j++)
    			if(now[i][j]!=aim[i][j]) cnt++;
    	return cnt;
    }
    bool dfs(int x,int y,int res){
    	if(res==0) return differ()==0;
    	if(differ()-1>res) return 0;
    	bool fg=0;
    	for(int i=0;i<8&&!fg;i++){
    		int nx=x+mv[i][0];
    		int ny=y+mv[i][1];
    		if(!inmap(nx,ny)) continue;
    		swap(now[x][y],now[nx][ny]);
    		fg=dfs(nx,ny,res-1);
    		swap(now[x][y],now[nx][ny]);
    	}
    	return fg;
    }
    int main(){
    	int T;scanf("%d",&T); 
    	char ch; bool fg;
    	while(T--){
    		fg=0;
    		for(int i=1;i<=5;i++)
    			for(int j=1;j<=5;j++){
    				scanf(" %c",&ch);
    				if(ch=='0') now[i][j]=0;
    				if(ch=='1') now[i][j]=1;
    				if(ch=='*') now[i][j]=2,sx=i,sy=j;
    			}
    		for(int i=0;i<=15;i++) 
    			if(dfs(sx,sy,i)){
    				fg=1; printf("%d
    ",i);
    				break;
    			}
    		if(!fg) printf("-1
    ");
    	}
    	return 0;
    }
    
        • BOZJ 1086 [SCOI2005]王室联邦

    是一种树分块么,长知识了。

    dfs时,维护一个栈,栈内维护访问过但还没确定所在省区的节点。
    当枚举完当前节点的某些儿子后,发现没确定所在省区的节点个数已经大于B了,
    就把他们划在一个省,弹出栈,省会城市为当前节点。

    然后把当前节点入栈,并返回到当前节点的父亲。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    struct edge{
    	int to,next;
    }e[1005*2];
    int head[1005],bel[1005],cap[1005],s[1005];
    int n,B,ent=2,top,cnt;
    void add(int u,int v){
    	e[ent]=(edge){v,head[u]};
    	head[u]=ent++;
    }
    void dfs(int u,int fa){
    	int k=top;
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(v==fa) continue;
    		dfs(v,u);
    		if(top-k>=B){
    			cap[++cnt]=u;
    			while(top!=k) bel[s[top--]]=cnt;
    		}
    	}
    	s[++top]=u;
    }
    int main(){
    	scanf("%d%d",&n,&B);
    	for(int i=1,u,v;i<n;i++){
    		scanf("%d%d",&u,&v);
    		add(u,v); add(v,u);
    	}
    	dfs(1,0);
    	while(top) bel[s[top--]]=cnt;
    	printf("%d
    ",cnt);
    	for(int i=1;i<=n;i++) printf("%d ",bel[i]);
    	printf("
    ");
    	for(int i=1;i<=cnt;i++) printf("%d ",cap[i]);
    	return 0;
    }
    
        • BOZJ 1087 [SCOI2005]互不侵犯King

    状压dp。
    当时看到棋盘的点那么多,就没想状压,然后没想出来。
    ●状压不一定非要把所有信息都保存啊,就本题而言,完全可以值在dp状态中记录某一行的状态信息。
    f[i][j][s]前i行中放了j个king,同时第i行的状态为s。
    直接枚举会超时。
    把所有合法状态先dfs枚举出来(没有相邻的1),只有不超过100种。
    并预处理任意两个状态是否可以上下相邻。
    然后就可以dp转移了。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define ll long long
    using namespace std;
    int sta[105],num[105];
    ll f[15][105][105],ans;
    bool rela[105][105];
    int n,m,cnt;
    void dfs(int p,int s,int l,int c){
    	if(p==n+1){
    		sta[++cnt]=s;
    		num[cnt]=c;
    		return;
    	}
    	dfs(p+1,s<<1,0,c);
    	if(!l) dfs(p+1,s<<1|1,1,c+1);
    }
    void relation(){
    	for(int i=1;i<=cnt;i++)
    		for(int j=1;j<=cnt;j++)
    			rela[i][j]=!((sta[i]&sta[j])||((sta[i]<<1)&sta[j])||(sta[i]&(sta[j]<<1)));
    }
    void dp(){
    	f[0][0][1]=1;
    	for(int i=0;i<=n;i++)
    		for(int j=0;j<=m;j++)
    			for(int k=1;k<=cnt;k++){
    				if(!f[i][j][k]) continue;
    				for(int l=1;l<=cnt;l++)	if(rela[k][l])
    					f[i+1][j+num[l]][l]+=f[i][j][k];
    			}
    	for(int i=1;i<=cnt;i++)
    		ans+=f[n][m][i];
    	printf("%lld",ans);
    } 
    int main(){
    	scanf("%d%d",&n,&m);
    	dfs(1,0,0,0);
    	relation();
    	dp();
    	return 0;
    }
    
        • BOZJ 1088 [SCOI2005]扫雷Mine

    列出式子,可以发现,确定了前两个位置后,后面的就可以确定了。
    所以for循环判断是否合法就好了。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    int num[10005],hve[10005];
    int n,ans;
    bool judge(){
    	for(int i=3;i<=n;i++){
    		hve[i]=num[i-1]-hve[i-1]-hve[i-2];
    		if(hve[i]<0) return 0;
    	}
    	if(hve[n]+hve[n-1]!=num[n]) return 0;
    	return 1;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&num[i]);
    	if(num[1]==0) ans+=judge();
    	else if(num[1]==1){
    		hve[1]=1; ans+=judge();
    		memset(hve,0,sizeof(hve));
    		hve[2]=1; ans+=judge();
    	}
    	else{
    		hve[1]=hve[2]=1;
    		ans+=judge();
    	}
    	printf("%d",ans);
    	return 0;
    }
      • 晚上 
        • BOZJ 1089 [SCOI2003]严格n元树

    f[i] 深度<=i的树的个数
    递推式: f[i]=f[i-1]^n+1
    然后答案为 f[d]-f[d-1]

    高精度搞搞。
    这题数据范围,呃,最大数据太大了,跑不出来。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define bit 10000
    using namespace std;
    struct Bigint{
    	int a[10000],len;
    	Bigint(){
    		memset(a,0,sizeof(a)); len=0;
    	}
    	void operator =(int rtm){
    		Bigint now;
    		if(!rtm) now.len=1;
    		else while(rtm) 
    			now.a[++now.len]=rtm%bit,rtm/=bit;
    		*this=now;
    	}
    	Bigint operator +(const Bigint &rtm) const{
    		Bigint now; now.len=max(len,rtm.len);
    		for(int i=1;i<=now.len;i++){
    			now.a[i]+=a[i]+rtm.a[i];
    			now.a[i+1]+=now.a[i]/bit;
    			now.a[i]%=bit;
    		}
    		while(now.a[now.len+1]) now.len++;
    		return now;
    	}
    	Bigint operator +(const int &val) const{
    		Bigint now,rtm; rtm=val;
    		now=(*this)+rtm;
    		return now;
    	}
    	Bigint operator -(const Bigint &rtm) const{
    		Bigint now; now.len=max(len,rtm.len);
    		for(int i=1;i<=now.len;i++){
    			now.a[i]+=a[i]-rtm.a[i];
    			if(now.a[i]<0)
    			now.a[i]+=bit,now.a[i+1]--;
    		}
    		while(!now.a[now.len]) now.len--;
    		return now;
    	}
    	Bigint operator -(const int &val) const{
    		Bigint now,rtm; rtm=val;
    		now=(*this)-rtm;
    		return now;
    	}
    	Bigint operator *(const Bigint &rtm) const{
    		Bigint now; now.len=len+rtm.len;
    		for(int i=1;i<=len;i++)
    			for(int j=1;j<=rtm.len;j++){
    				now.a[i+j-1]+=a[i]*rtm.a[j];
    				now.a[i+j]+=now.a[i+j-1]/bit;
    				now.a[i+j-1]%=bit;
    			}
    		while(!now.a[now.len]) now.len--;
    		return now;
    	}
    	Bigint operator *(const int &val) const{
    		Bigint now,rtm; rtm=val;
    		now=(*this)*rtm;
    		return now;
    	}
    	Bigint operator ^(int val) const{
    		Bigint now,bas;
    		now=1; bas=(*this);
    		while(val){
    			if(val&1) now=now*bas;
    			bas=bas*bas;
    			val>>=1;
    		}
    		return now;
    	}
    	void print(){
    		printf("%d",a[len]);
    		for(int i=len-1;i>=1;i--)
    			printf("%04d",a[i]);
    	}
    };
    int n,d;
    int main(){
    	Bigint a,b;
    	a=0; b=1;
    	scanf("%d%d",&n,&d);
    	for(int i=1;i<=d;i++)
    		a=b,b=(a^n)+1;
    	b=b-a;
    	b.print();
    	return 0;
    }
    
        • BOZJ 1090 [SCOI2003]字符串折叠

    区间dp,记忆化实现。
    f[l][r]表示字符串l~r最小可以被压缩的长度。
    转移:
    1). 普通的拼接        f[l][r]=min(f[l][r],f[l][i]+f[i+1][r])
    2).    如果区间重复    f[l][r]=min(f[l][r],f[l][i]+2+cal((r-l+1)/(i-l+1)))
        上式的"2"是括号长度,cal是计算十进制数的字符长度。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    char s[105];
    bool vis[105][105];
    int f[105][105];
    int cal(int x){
    	int cnt=0;
    	while(x) cnt++,x/=10;
    	return cnt;
    }
    bool repeat(int l,int r,int L,int R){
    	if((R-L+1)%(r-l+1)) return 0;
    	for(int i=L,j=l;i<=R;i++,j++){
    		if(j>r) j=l;
    		if(s[i]!=s[j]) return 0;
    	}
    	return 1;
    }
    int dp(int l,int r){
    	if(l==r) return 1;
    	if(vis[l][r]) return f[l][r];
    	vis[l][r]=1; int res=r-l+1;
    	for(int i=l;i<r;i++){
    		res=min(res,dp(l,i)+dp(i+1,r));
    		if(repeat(l,i,i+1,r))
    		res=min(res,dp(l,i)+2+cal((r-l+1)/(i-l+1)));
    	}
    	return f[l][r]=res;
    }
    int main(){
    	scanf("%s",s+1); int len=strlen(s+1);
    	printf("%d",dp(1,len));
    	return 0;
    }
    
  • 相关阅读:
    设计模式学习笔记--原型模式
    设计模式学习笔记--工厂方法模式
    复制、粘贴一个物体的所有组件
    设计模式学习笔记--装饰模式
    模板方法模式(TemplateMethod)
    FreeSql 与 SqlSugar 性能测试(增EFCore测试结果)
    FreeSql 新查询功能介绍
    FreeSql 过滤器使用介绍
    非常贴心的轮子 FreeSql
    .NETCore 下支持分表分库、读写分离的通用 Repository
  • 原文地址:https://www.cnblogs.com/zj75211/p/7757796.html
Copyright © 2020-2023  润新知