2013金山西居挑战赛初赛1—转自blog.csdn.net/asdfgh0308/
刚水了这场比赛:2013金山西山居创意游戏程序挑战赛——初赛(1)。
简要题解如下:
1001 魔法串:
问第二个串能不能变成第一个串。
显然的DP问题。dp[i][j]表示第二串前j个变成第一串前i个是否可行。
转移就是
if (dp[i][j-1]==1) dp[i][j]=1;
if (dp[i-1][j-1]==1&&g[t[j]][s[i]]==1){dp[i][j]=1;}
g[a][b]为1表示a字符能变成b字符。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define NN 1010 int cas,tcas,i,g[300][300],j,l1,l2,dp[NN][NN],m; char s[NN],t[NN],sa[50],sb[50]; int main(){ scanf("%d",&tcas); for(cas=1;cas<=tcas;++cas){ scanf("%s%s",s+1,t+1); s[0]=t[0]='*'; scanf("%d",&m); memset(g,0,sizeof(g)); for(i='a';i<='z';++i){g[i][i]=1;} for(i=1;i<=m;++i){ scanf("%s%s",sa,sb); g[sa[0]][sb[0]]=1; } memset(dp,0,sizeof(dp)); l1=strlen(s)-1;l2=strlen(t)-1; for(i=0;i<=l2;++i) dp[0][i]=1; for(i=1;i<=l1;++i){ for(j=i;j<=l2;++j){ if (dp[i][j-1]==1){ dp[i][j]=1; } if (dp[i-1][j-1]==1&&g[t[j]][s[i]]==1){ dp[i][j]=1; } } } printf("Case #%d: ",cas); printf("%s\n",dp[l1][l2]?"happy":"unhappy"); } return 0; }
1002 比赛难度:
人品不错,前几天刚在中南月赛中做了一个类似的题目:求第m小的三个素数相乘所得的数。(想想怎么做?)
显然,m很小,很容易想到构造的方式。问题是怎么构造出比较小的可能的状态。
用优先队列类似宽搜的方式,每次加入最有可能的几个状态。然后在队列中取最小的状态继续搜索。
将题目按照难度排序。
这题状态可以记录为两个值,a:当前所取的总难度和,b:最后取的一道题的题号。
那么从这个状态拓展的下一个最有可能的状态就是
1:去掉第b道题,取第b+1道题;
2:取现在状态所取的所有题加上第b+1道题。
这样搜索下去就能不重复不遗漏地取到所有的状态。而每次取出一个状态最多只会加入两个状态,这样时间空间都是0(M)。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<map> #include<algorithm> using namespace std; #define NN 20100 struct node{ int a,b; bool operator <(const node &y)const{ if (a==y.a) return b>y.b; else return a>y.a; } }tn,un; priority_queue<node> q; int v[NN]; int main(){ int tcas,cas,n,m,i,a,b,tot,ans; scanf("%d",&tcas); for(cas=1;cas<=tcas;++cas){ scanf("%d%d",&n,&m); for(i=1;i<=n;++i){ scanf("%d",&v[i]); } sort(v+1,v+n+1); while(!q.empty()) q.pop(); tn.a=v[1];tn.b=1; q.push(tn); tot=0; while(!q.empty()){ un=q.top();q.pop(); tot++; //printf("%d %d\n",un.a,un.b); if (tot>=m) {ans=un.a;break;} a=un.a;b=un.b; if (b<n){ tn.a=a-v[b]+v[b+1];tn.b=b+1; q.push(tn); tn.a=a+v[b+1];tn.b=b+1; q.push(tn); } } printf("Case #%d: %d\n",cas,ans); } return 0; }
1003 CD操作:
其实CD这个命令可以直接输入目标文件夹的绝对路径,这样一次就够了我会乱说?
这题的cd只能返回上一层,而向下可以一次到达。
显然的LCA问题,求出两个目录的最小公共祖先目录,一直向上找到这个目录为止,再一次(或是0次)走到目标目录即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<map> using namespace std; #define NN 100005 #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) int mr[NN*2][18],first[NN],next[NN],w[NN],R[NN]; int tote,totn; int i,deg[NN],a,b,cas,n,root,ans; map<string,int> mp; char ts1[50],ts2[50]; string str1,str2; struct node{ int o,dep; }aa[NN*2]; void lcadfs(int now,int dep){ aa[++totn].o=now; aa[totn].dep=dep; int e; for(e=first[now];e!=-1;e=next[e]){ lcadfs(w[e],dep+1); aa[++totn].o=now; aa[totn].dep=dep; } } void rmqinit(){ int m=-1,k=totn; while(k) {m++;k>>=1;} int i,j; for(i=1;i<=totn;i++) {mr[i][0]=i;}//mr记录最小值位置 for(i=1;i<=m;i++){ for(j=1;j<=totn;j++){ mr[j][i]=mr[j][i-1]; if (j+(1<<(i-1))<=totn) { if (aa[mr[j][i]].dep>aa[mr[j+(1<<(i-1))][i-1]].dep){ mr[j][i]=mr[j+(1<<(i-1))][i-1]; } } } } } int rmqmin(int l,int r){ int m=-1,k=r+1-l; while(k) {k>>=1;m++;} if (aa[mr[l][m]].dep<aa[mr[r+1-(1<<m)][m]].dep){ return mr[l][m]; } else return mr[r+1-(1<<m)][m]; } int main(){ int m,tots; scanf("%d",&cas); while(cas--){ mp.clear(); scanf("%d%d",&n,&m); for(i=1;i<=n;i++){first[i]=-1;deg[i]=0;} tote=0; tots=0; for(i=1;i<=n-1;i++){ scanf("%s%s",ts1,ts2); str1=ts1;str2=ts2; if (mp[str1]==0) mp[str1]=++tots; if (mp[str2]==0) mp[str2]=++tots; b=mp[str1];a=mp[str2]; tote++; next[tote]=first[a]; first[a]=tote; w[tote]=b; deg[b]++; } for(i=1;i<=n;i++) if (deg[i]==0) {root=i;break;} totn=0; lcadfs(root,0); memset(R,0,sizeof(R)); for(i=1;i<=totn;i++){ if (!R[aa[i].o]) R[aa[i].o]=i; } rmqinit(); int tmp; for(i=1;i<=m;++i){ scanf("%s%s",ts1,ts2); str1=ts1;str2=ts2; a=mp[str1];b=mp[str2]; if (R[a]>=R[b]) tmp=rmqmin(R[b],R[a]); else tmp=rmqmin(R[a],R[b]); ans=aa[R[a]].dep-aa[tmp].dep; if (aa[tmp].o!=b) ans+=1; printf("%d\n",ans); } } return 0; }