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;
}
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas

浙公网安备 33010602011771号