17.10.12
- 上午
- BZOJ 真的好难啊、、、做一个就一两个h过去了。
- BZOJ 1019 [SHOI2008]汉诺塔
(算了,写得太宽了,还是贴截图吧(双击看大图哦))
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
ll f[3][105];
int g[3][105],n;
char to[10][3];
int main(){
scanf("%d",&n);
for(int i=1,a,b;i<=6;i++){
scanf(" %c %c",&to[i][1],&to[i][2]);
a=to[i][1]-'A'+1; b=to[i][2]-'A'+1;
if(!g[a][1]) g[a][1]=b,f[a][1]=1;
}
for(int i=2;i<=n;i++){
for(int x=1,y,z;x<=3;x++){
y=g[x][i-1]; //x上的前i-1个应该移到y去
z=6-x-y; //x上的第i个应该移到到z去
f[x][i]=f[x][i-1]+1;//上面两个移动的代价
if(g[y][i-1]==z) //如果y上的i-1个应该移到z上,就直接移动
f[x][i]+=f[y][i-1],g[x][i]=z;
else //否则,y上的i-1个应该先移动到x上,然后z上的第i个移到y上,x上的i-1个再移到y上
f[x][i]+=f[y][i-1]+1+f[x][i-1],g[x][i]=y;
}
}
printf("%lld",f[1][n]);
return 0;
}
- 九度 1535 重叠的最长子串
Ztraveler让我去做、、、、、、
想了两个办法,
一个是受bzoj 的启发,可以二分+hash做吧
我写的另一种,是用的kmp去匹配。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 1000005
using namespace std;
char t[MAXN],s[MAXN];
int nxt[MAXN],ans;
int doit(char *A,char *B){
int la=strlen(A),lb=strlen(B);
nxt[0]=-1; int j=0,k=-1;
while(j<lb){
if(k==-1||B[k]==B[j]) k++,j++,nxt[j]=k;
else k=nxt[k];
}
j=0; int i=0;
while(i<la){
if(A[i]==B[j]||j==-1){
i++,j++;
if(i==la) return j;
}
else j=nxt[j];
}
return 0;
}
int main(){
while(scanf("%s%s",t,s)!=EOF){
ans=doit(t,s);
printf("%d\n",ans);
}
return 0;
}
- 下午
- BZOJ 1021 [SHOI2008]Debt 循环的债务
又是神奇dp(bzoj的dp题总是摧残我)
首先有一个贪心的想法,对于某一种票子
如果一开始三个人分别有A ,B ,C张
最后通过交换变成了分别有A',B',C'张
即变化量分别为(有正负)da ,db ,dc
那么最少的交换次数为 (abs(da)+abs(db)+ads(dc))/2
蛤?
我们令pa=abs(da),pb=abs(db),pa=abs(dc),psum=pa+pb+pc
显然一次票子的转移,会带来一个人的d变小,另一个人的d变大(再强调一次,d有正负)
按照贪心策略,是让d>0的人把票子给d<0的人,那么两个人对应的p分别会减小1
则psum会减小2
这表明按照贪心策略,一次票子转移对应这psum会减小2
所以最少的交换次数为 (abs(da)+abs(db)+ads(da+db))/2 (abs(dc)==abs(da+db))
依次考虑每种票子的交换
f[i][k1][k2]:表示
前i种票子进行交换后,第一个人有k1那么多钱,第二个人有k2那么多钱时的最少交换次数
所以枚举票子种类(确定三个人的 A ,B ,C)
分别枚举两个个人的钱数
分别枚举两个人有多少当前票子(得出A' ,B' ,C')
然后用上贪心策略得出的最小转移代价,就可以刷表法转移了。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
const int val[10]={0,1,5,10,20,50,100};
int aim[5],tot[5],off[5],hav[5][10],cnt[10];
int f[2][1005][1005],cur,sum;
int abs(int x){
return x>0?x:-x;
}
int main(){
scanf("%d%d%d",&off[1],&off[2],&off[3]);
for(int i=1;i<=3;i++)
for(int j=6;j>=1;j--){
scanf("%d",&hav[i][j]);
tot[i]+=hav[i][j]*val[j]; cnt[j]+=hav[i][j];
}
sum=tot[1]+tot[2]+tot[3];
aim[1]=tot[1]-off[1]+off[3];
aim[2]=tot[2]-off[2]+off[1];
if(aim[1]<0||aim[2]<0||sum-aim[1]-aim[2]<0){printf("impossible");return 0;}
memset(f[cur],0x3f,sizeof(f[cur]));
f[cur][tot[1]][tot[2]]=0;
for(int i=1;i<=6;i++){
memset(f[cur^1],0x3f,sizeof(f[cur^1]));
for(int k1=0;k1<=sum;k1++)
for(int k2=0;k1+k2<=sum;k2++){
if(f[cur][k1][k2]==INF) continue;
f[cur^1][k1][k2]=min(f[cur^1][k1][k2],f[cur][k1][k2]);
for(int h1=0;h1<=cnt[i];h1++)
for(int h2=0;h1+h2<=cnt[i];h2++){
int d1=h1-hav[1][i],d2=h2-hav[2][i];
int n1=k1+d1*val[i],n2=k2+d2*val[i];
if(n1<0||n2<0||sum-n1-n2<0) continue;
int dj=(abs(d1)+abs(d2)+abs(d1+d2))/2;
f[cur^1][n1][n2]=min(f[cur^1][n1][n2],f[cur][k1][k2]+dj);
}
}
cur^=1;
}
if(f[cur][aim[1]][aim[2]]==INF){printf("impossible");return 0;}
printf("%d",f[cur][aim[1]][aim[2]]);
return 0;
}
- 晚上
- BZOJ 1022 [SHOI2008]小约翰的游戏John
真的看不懂证明,就只有先记结论
Anti-SG游戏定义
1、决策集合为空的操作者胜。
2、其余规则与SG游戏一致。
SJ定理
对于任意一个Anti-SG游戏,如果定义所有子游戏的SG值为0时游戏结束,先手必胜的条件:
1、游戏的SG值为0且所有子游戏SG值均不超过1。
2、游戏的SG值不为0且至少一个子游戏SG值超过1。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int SG,n,T;
bool allin,oneout; //1
int main(){
scanf("%d",&T);
while(T--){
SG=0;
allin=1; oneout=0;
scanf("%d",&n);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
if(x>1) allin=0,oneout=1;
SG^=x;
}
if((!SG&&allin)||(SG&&oneout)) printf("John\n");
else printf("Brother\n");
}
return 0;
}
- BZOJ
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas


浙公网安备 33010602011771号