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