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\n",i);
				break;
			}
		if(!fg) printf("-1\n");
	}
	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\n",cnt);
	for(int i=1;i<=n;i++) printf("%d ",bel[i]);
	printf("\n");
	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;
}
posted @ 2017-10-30 21:58  *ZJ  阅读(197)  评论(0编辑  收藏  举报