Codeforces Round #106

  B.Martian_Clock

  容易犯错的地方是判断无解的情况, 如果只有1位数(一个字母或者一个数字)则无解,但是无解有可能需要输出-1或者0,需要更加精确地判断.

  

关键代码
if(lenh-ph<=1 && lenm-pm<=1)
{
if(hour[ph]>='O' && hour[ph]<='Z'){printf("0\n");return 0;}
printf("-1\n");
return 0;
}
for(base=2;;base++)
{
if(ok(ph,pm,base))
{
nh=to_num(ph,hour,base);
nm=to_num(pm,minu,base);
if(nh<24 && nm<60)
{
if(flag)printf(" %d",base);
else printf("%d",base);
flag=1;
}
else
break;
}
}
if(!flag)printf("0\n");

 

  C.Division_into_Teams

  需要构造2个球队,使人数之差<=1,技巧数之差不超过技巧数最大的个人.

  首先需要排序无疑,之后会面临2种策略,一大一小地取,和逐个地取,第1种取法有些乱搞的感觉,反正我木有想到证明方法,

  第2种取法的描述和正确性:

    排序后,将有一对相同技巧数的人分派到不同的球队,这样处理后再开始处理一队拥有不同技巧数的人;

    显然在处理前2队实力是均衡的,而处理过程中在某人i取之前,必有技巧数和totali <= totalj,且totalj-totali<=i当前取的值

    于是i取完后,totali会反超totalj且差值为si - (totalj-totali),小于si.

 

  D.Coloring_Brackets 

  很重要的一句话是"such that it is possible to obtain a correct mathematical expression from it, inserting

  numbers and operators between the brackets"

  排除了括号嵌套的匹配情况(如: (  [...) ]  ),如果考虑了嵌套匹配,貌似会变得非常复杂,我在这里纠结了好久= =.

  于是对于每个串都有唯一的匹配方式,用(l,r,cl,cr)来表示l~r段的染色方案,其中cl和cr,记录的是外层l-1和r+1的染色,

  求染色方案时调用dp(0,strlen(str),0,0);求一个括号串(......)的染色方案,它可能被分解为:

  1. (L...)(...)R    继续求(L...)段的染色方案res1,和 (...)R段的染色方案res2,结果为res1*res2

  或者

  2.(L...)R   正好外部为一组匹配的括号,枚举括号组(l,r)的染色后,继续求(l+1,r-1)段的染色方案.

  若r=l+1 则为"()"不能再分,返回1.

 

  

View Code
#include<stdio.h>
#include<string.h>
#define inf 1000000007
long f[701][701][3][3];
int ma[701],len;
char str[701];
int vis[701];
void init_ma(int cur)
{
int i=cur+1;
if(ma[cur])return;
while(1)
{
if(str[i]==')' && !vis[i])
{
vis[i]=1;
ma[cur]=i;
return;
}
else
if(str[i]=='(')
init_ma(i);
i++;
}
}
long dp(int l,int r,int cl,int cr)
{
long &res=f[l][r][cl][cr];
int i,k;
k=ma[l];
if(res>=0)return res;
if(l>=r)return 1;
res=0;
for(i=1;i<3;i++)
{
if(k==r)//()
{
if(i!=cr)
res=(res+(long long)dp(l+1,r-1,0,i))%inf;//ȾɫΪ0,i
if(i!=cl)
res=(res+(long long)dp(l+1,r-1,i,0))%inf;//ȾɫΪi,0
}
else//()()
{
res=(res+(long long)dp(l+1,k-1,0,i)*dp(k+1,r,i,cr))%inf;//ȾɫΪ0,i
if(i!=cl)
res=(res+(long long)dp(l+1,k-1,i,0)*dp(k+1,r,0,cr))%inf;//ȾɫΪi,0
}
}
return res;
}
int main()
{
int i;
long ans;
while(scanf("%s",str)!=EOF)
{
len=strlen(str);
memset(f,-1,sizeof(f));
memset(vis,0,sizeof(vis));
for(i=0;i<len;i++)
if(str[i]=='(')
init_ma(i);
ans=dp(0,len-1,0,0);
printf("%ld\n",ans);
}
return 0;
}

 

 E.Martian_Strings

 KMP算法的应用.

 前几天刚学的kmp,只做过几个模板题,而没有实践过,无限饥渴地把道题调戏了一个下午,最后发现wa的原因是数组开小了.= =

-------------------------------------我是分界线-----------------------------------------------------------------

 关键思想是定义pre[i]表示从前往后数第一处匹配了模板的前缀的地方,该前缀的长度为i,pre[i]表示此时在文本串的下标;

 类似地定义一个suf[i]表示从后往前第一处匹配了模板后缀的地方,该后缀长度为i,suf[i]表示此时文本串的下标;

 需要特别处理的是模式串长度为1的情况,按照题意,这种是无法看到的,而kmp的过程中可能把pre[0](或者suf[0])更新.

 可以添加条件if (j>0)更新suf[j],pre[j],或者输入后直接判断,若==1则continue;

 

kmp
#include<stdio.h>
#include<string.h>
#define size 100010
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)
long pre[size];
long suf[size];
long next_pre[size];
long next_suf[size],len,n;
char t[size];
char p[size];
void init_next()
{
long i,j;
next_pre[0]=-1;
for(i=0,j=-1;i<len;)
{
while(j>=0 && p[i]!=p[j])j=next_pre[j];
i++;j++;
next_pre[i]=j;
}
next_suf[0]=-1;
for(i=0,j=-1;i<len;)
{
while(j>=0 && p[len-1-i]!=p[len-1-j])j=next_suf[j];
i++;j++;
next_suf[i]=j;
}
}
void kmp()
{
long i,j;
for(i=0;i<=len;i++)pre[i]=5000005;
for(i=0,j=0;i<n;i++)
{
while(j>=0 && p[j]!=t[i])
j=next_pre[j];
j++;if(j>0)pre[j]=min(pre[j],i);
}

for(i=0;i<=len;i++)suf[i]=-1;
for(i=0,j=0;i<n;i++)//j为匹配长度
{
while(j>=0 && p[len-1-j]!=t[n-1-i])
j=next_suf[j];
j++;if(j>0)suf[j]=max(suf[j],n-1-i);
}
}
int main()
{
long i,j,np;
long count;
// freopen("test.txt","r",stdin);
scanf("%s",t);
n=strlen(t);
scanf("%ld",&np);
for(i=0,count=0;i<np;i++)
{
scanf("%s",p);
len=strlen(p);
if(len==1)continue;
init_next();
kmp();
for(j=1;j<len;j++)
if(suf[j]>pre[len-j])
{
count++;
break;
}
}
printf("%ld\n",count);
return 0;
}

 

转载于:https://www.cnblogs.com/eggeek/archive/2012/02/12/2348528.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值