2015 NOIP提高组 复赛解题报告 C++

目录(?)[+]

Day 1

T1  直接根据题目描述的模拟就可以了,水。
【代码】
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. using namespace std;  
  5. int n,m,i,j,num;  
  6. int a[50][50];  
  7. int main(){  
  8.     scanf("%d",&n);  
  9.     m=(n/2)+1;  
  10.     a[1][m]=1;  
  11.     num=1;  
  12.     i=1; j=m;  
  13.     while (num<n*n){  
  14.         if (i==1&&j!=n) {a[n][j+1]=++num; i=n; ++j;}  
  15.         else if (i!=1&&j==n) {a[i-1][1]=++num; --i; j=1;}  
  16.         else if (i==1&&j==n) {a[i+1][j]=++num; ++i;}  
  17.         else if (i!=1&&j!=n){  
  18.             if (!a[i-1][j+1]) {a[i-1][j+1]=++num; --i; ++j;}  
  19.             else {a[i+1][j]=++num; ++i;}  
  20.         }  
  21.     }  
  22.     for (int i=1;i<=n;++i){  
  23.         for (int j=1;j<n;++j)  
  24.           printf("%d ",a[i][j]);  
  25.         printf("%d\n",a[i][n]);  
  26.     }  
  27.     return 0;  
  28. }  


T2  读了一遍题就看出来是最小环。刚开始忽略了有好几个联通块,大数据跑不对。最后写了个并查集+dfs,AC。水。
【代码】
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. #define inf 2100000000  
  5. #define N 200005  
  6. using namespace std;  
  7. int n,ans,y;  
  8. int f[N],next[N],h[N];  
  9. bool b[N];  
  10. inline int in(){  
  11.     int x=0; char ch=getchar();  
  12.     while (ch<'0'||ch>'9') ch=getchar();  
  13.     while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();  
  14.     return x;  
  15. }  
  16. int find(int x){  
  17.     if (x==f[x]) return x;  
  18.     f[x]=find(f[x]);  
  19.     return f[x];  
  20. }  
  21. void merge(int x,int y){  
  22.     int f1=find(x);  
  23.     int f2=find(y);  
  24.     f[f1]=f2;  
  25. }  
  26. void dfs(int x,int dep){  
  27.     b[x]=true; h[x]=dep;  
  28.     if (!b[next[x]])  
  29.       dfs(next[x],dep+1);  
  30.     else{  
  31.         int k=h[x]-h[next[x]]+1;  
  32.         ans=min(ans,k);  
  33.     }  
  34. }  
  35. int main(){  
  36.     n=in(); ans=inf;  
  37.     for (int i=1;i<=n;++i)  
  38.       f[i]=i;  
  39.     for (int i=1;i<=n;++i){  
  40.         y=in();  
  41.         next[i]=y;  
  42.         if (find(i)!=find(y))  
  43.           merge(i,y);  
  44.         else{  
  45.             memset(b,0,sizeof(b));  
  46.             dfs(i,1);  
  47.         }  
  48.     }  
  49.     printf("%d",ans);  
  50.     return 0;  
  51. }  

T3  时限2s内存1G就知道这道题一定很神。刚开始看到了之后一点思路都没有,以为是dp但是发现状态太多压不下。而且还有多组数据一旦跑不对就挂了。30分打表。
后来听到TA大爷说就是个dfs,把每一种情况编上号之后搜索就可以了。
好像也有dfs+dp的做法,不过我觉得这个更好理解。
1、单顺子 2、双顺子 3、三顺子 4、三带一、三带二  5、四带二、四带两对
由于每张牌的数量最多有4张,所以能保证这一种牌只要有就一定能一次打出去,所以出牌总次数的上限就是手牌的种类数。而每一次dfs都有一个新的上限,就是当前步数+现有的手牌的种类数。dfs过程中要进行最优化剪枝。

2016.10.28update
这题比较好理解,不过忘得差不多干净了之后又重写了一遍。
觉得现在的码风比原来好很多,所以把代码换掉_(:з」∠)_

【代码】
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. using namespace std;  
  5.   
  6. int T,n,x,y,ans;  
  7. int a[20];  
  8.   
  9. void clear()  
  10. {  
  11.     memset(a,0,sizeof(a));  
  12.     ans=0;  
  13. }  
  14. bool check()  
  15. {  
  16.     for (int i=1;i<=15;++i)  
  17.         if (a[i]) return false;  
  18.     return true;  
  19. }  
  20. void dfs(int dep)  
  21. {  
  22.     if (dep>ans) return;  
  23.     if (check())  
  24.     {  
  25.         ans=min(ans,dep);  
  26.         return;  
  27.     }  
  28.     int sum=0;  
  29.     for (int i=1;i<=13;++i)  
  30.         if (a[i]) sum++;  
  31.     if (a[14]+a[15]) sum++;  
  32.     ans=min(ans,dep+sum);  
  33.     // 1 单顺子 2 双顺子 3 三顺子 4 三带一、三带二 5 四带二、四带两对  
  34.     for (int kind=1;kind<=5;++kind)  
  35.     {  
  36.           
  37.         if (kind==1)  
  38.         {  
  39.             for (int i=1;i<=8;++i)  
  40.                 if (a[i])  
  41.                 {  
  42.                     bool flag=true;  
  43.                     for (int j=i+1;j<=i+3;++j)  
  44.                         if (!a[j]) {flag=false;break;}  
  45.                     if (!flag) continue;  
  46.                       
  47.                     for (int j=i+4;j<13&&a[j];++j)  
  48.                     {  
  49.                         for (int k=i;k<=j;++k) --a[k];  
  50.                         dfs(dep+1);  
  51.                         for (int k=i;k<=j;++k) ++a[k];  
  52.                     }  
  53.                 }  
  54.         }  
  55.         if (kind==2)  
  56.         {  
  57.             for (int i=1;i<=10;++i)  
  58.                 if (a[i]>=2&&a[i+1]>=2)  
  59.                     for (int j=i+2;j<13&&a[j]>=2;++j)  
  60.                     {  
  61.                         for (int k=i;k<=j;++k) a[k]-=2;  
  62.                         dfs(dep+1);  
  63.                         for (int k=i;k<=j;++k) a[k]+=2;  
  64.                     }  
  65.         }  
  66.         if (kind==3)  
  67.         {  
  68.             for (int i=1;i<=11;++i)  
  69.                 if (a[i]>=3)  
  70.                 {  
  71.                     if (a[i+1]<3) continue;  
  72.                       
  73.                     for (int j=i;j<13&&a[j]>=3;++j)  
  74.                     {  
  75.                         for (int k=i;k<=j;++k) a[k]-=3;  
  76.                         dfs(dep+1);  
  77.                         for (int k=i;k<=j;++k) a[k]+=3;  
  78.                     }  
  79.                 }  
  80.         }  
  81.           
  82.         if (kind==4)  
  83.         {  
  84.             for (int i=1;i<=13;++i)  
  85.                 if (a[i]>=3)  
  86.                 {  
  87.                     a[i]-=3;  
  88.                     for (int j=1;j<=15;++j)  
  89.                         if (a[j])  
  90.                         {  
  91.                             --a[j];  
  92.                             dfs(dep+1);  
  93.                             ++a[j];  
  94.                         }  
  95.                       
  96.                     for (int j=1;j<=15;++j)  
  97.                         if (a[j]>=2)  
  98.                         {  
  99.                             a[j]-=2;  
  100.                             dfs(dep+1);  
  101.                             a[j]+=2;  
  102.                         }  
  103.                     a[i]+=3;  
  104.                 }  
  105.               
  106.         }  
  107.         if (kind==5)  
  108.         {  
  109.               
  110.             for (int i=1;i<=15;++i)  
  111.                 if (a[i]>=4)  
  112.                 {  
  113.                     a[i]-=4;  
  114.                     for (int j=1;j<=15;++j)  
  115.                         if (a[j])  
  116.                         {  
  117.                             --a[j];  
  118.                             for (int k=j;k<=15;++k)  
  119.                                 if (a[k])  
  120.                                 {  
  121.                                     --a[k];  
  122.                                     dfs(dep+1);  
  123.                                     ++a[k];  
  124.                                 }  
  125.                             ++a[j];  
  126.                         }  
  127.                     a[i]+=4;  
  128.                 }  
  129.             for (int i=1;i<=15;++i)  
  130.                 if (a[i]>=4)  
  131.                 {  
  132.                     a[i]-=4;  
  133.                     for (int j=1;j<=15;++j)  
  134.                         if (a[j]>=2)  
  135.                         {  
  136.                             a[j]-=2;  
  137.                             for (int k=j;k<=15;++k)  
  138.                                 if (a[k]>=2)  
  139.                                 {  
  140.                                     a[k]-=2;  
  141.                                     dfs(dep+1);  
  142.                                     a[k]+=2;  
  143.                                 }  
  144.                             a[j]+=2;  
  145.                         }  
  146.                     a[i]+=4;  
  147.                 }  
  148.         }  
  149.     }  
  150. }  
  151. int main()  
  152. {  
  153.     scanf("%d%d",&T,&n);  
  154.     while (T--)  
  155.     {  
  156.         clear();  
  157.         for (int i=1;i<=n;++i)  
  158.         {  
  159.             scanf("%d%d",&x,&y);  
  160.             if (!x) a[y+13]++;  
  161.             if (x==1||x==2) a[x+11]++;  
  162.             if (x>=3) a[x-2]++;  
  163.         }  
  164.         for (int i=1;i<=13;++i)  
  165.             if (a[i]) ans++;  
  166.         if (a[14]+a[15]) ans++;  
  167.         dfs(0);  
  168.         printf("%d\n",ans);  
  169.     }  
  170. }  


Day2

T1  二分答案+贪心枚举,考前做过原题,但是还是调了一会,水。
【代码】
[cpp]  view plain  copy
  1. #include<algorithm>  
  2. #include<iostream>  
  3. #include<cstring>  
  4. #include<cstdio>  
  5. #define N 50005  
  6. using namespace std;  
  7. int L,n,m,l,r,mid,num;  
  8. int a[N];  
  9. int ok(int x){  
  10.     int ans=0,k=0;  
  11.     for (int i=1;i<=n+1;++i)  
  12.       if (a[i]-a[k]<x)  
  13.         ans++;  
  14.       else k=i;  
  15.     return ans;  
  16. }  
  17. int main(){  
  18.     scanf("%d%d%d",&L,&n,&m);  
  19.     for (int i=1;i<=n;++i)  
  20.       scanf("%d",&a[i]);  
  21.     sort(a+1,a+n+1);  
  22.     a[0]=0; a[n+1]=L;  
  23.     l=0; r=L;  
  24.     while(l<=r){  
  25.         mid=(l+r)/2;  
  26.         num=ok(mid);  
  27.         if (num<=m) l=mid+1;  
  28.         else r=mid-1;  
  29.     }  
  30.     printf("%d",l-1);  
  31. }  

T2  一眼就能看出来是dp,但是dp学的不好,想了一会没大有思路,就只写了30分的部分分。dfs没跑出来就只得了10分。
动规的思路看了ShallWe的代码才勉强理解,要是让我自己想是绝对想不到的,因为我当时连前缀和优化是什么都不怎么清楚。
f[i][j][k][l]表示第一个串的前i个,第二个串的前j个,分成k段,还有一维是前缀和,也可理解为选还是不选。注意这里的第一维要加一个滚动数组。

【代码】
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. #define N 1005  
  5. #define M 205  
  6. #define p 1000000007  
  7. using namespace std;  
  8. char s1[N],s2[M];  
  9. int n,m,k;  
  10. int f[2][M][M][2];  
  11. inline void in(){  
  12.     char ch=getchar();  
  13.     while (ch<'a'||ch>'z') ch=getchar();  
  14.     s1[1]=ch;  
  15.     for (int i=2;i<=n;++i) s1[i]=getchar();  
  16.     ch=getchar();  
  17.     while (ch<'a'||ch>'z') ch=getchar();  
  18.     s2[1]=ch;  
  19.     for (int i=2;i<=m;++i) s2[i]=getchar();  
  20. }  
  21. int main(){  
  22.     scanf("%d%d%d",&n,&m,&k);  
  23.     in();  
  24.     f[0][0][0][0]=1;  
  25.     f[1][0][0][0]=1;  
  26.     for (int i=1;i<=n;++i)  
  27.       for (int j=1;j<=min(i,m);++j)  
  28.         for (int l=1;l<=k;++l){  
  29.             int now=i&1,last=(i-1)&1;  
  30.             if (s1[i]==s2[j]){  
  31.                 f[now][j][l][1]=((f[last][j-1][l-1][0]+f[last][j-1][l-1][1])%p+f[last][j-1][l][1])%p;  
  32.                 f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;  
  33.             }  
  34.             else{  
  35.                 f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;  
  36.                 f[now][j][l][1]=0;  
  37.             }  
  38.         }  
  39.     printf("%d",(f[n&1][m][k][0]+f[n&1][m][k][1])%p);  
  40. }  

2016.10.27update
注意上文的“勉强理解”,今天把当时写的代码翻出来看看然后一脸懵逼,推翻重写。
可能是最近这种类型的dp做的比较多,现在看这道题就比较简单了。
同样是把代码先贴出来,详细题解 戳这里
撒花撒花~~
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. using namespace std;  
  5. #define N 1005  
  6. #define M 205  
  7. #define Mod 1000000007  
  8.   
  9. int n,m,p;  
  10. int f[2][N][M],s[2][N][M],g[N][M];  
  11. char a[N],b[M];  
  12.   
  13. void init()  
  14. {  
  15.     for (int i=1;i<=n;++i)  
  16.         for (int j=1;j<=m;++j)  
  17.         {  
  18.             g[i][j]=min(i,j);  
  19.             for (int k=1;k<=min(i,j);++k)  
  20.                 if (a[i-k+1]!=b[j-k+1])  
  21.                 {  
  22.                     g[i][j]=k-1;  
  23.                     break;  
  24.                 }  
  25.         }  
  26. }  
  27. int main()  
  28. {  
  29.     scanf("%d%d%d\n",&n,&m,&p);  
  30.     gets(a+1);gets(b+1);  
  31.       
  32.     init();  
  33.     for (int i=1;i<=m;++i)  
  34.         for (int j=i;j<=n;++j)  
  35.         {  
  36.             f[1][j][i]=f[1][j-1][i];  
  37.             if (g[j][i]==i) f[1][j][i]++;  
  38.             s[1][j][i]=s[1][j-1][i-1]+f[1][j][i];  
  39.         }  
  40.               
  41.     for (int i=2;i<=p;++i)  
  42.     {  
  43.         memset(f[i&1],0,sizeof(f[i&1]));  
  44.         memset(s[i&1],0,sizeof(s[i&1]));  
  45.         for (int j=1;j<=n;++j)  
  46.             for (int k=1;k<=m;++k)  
  47.             {  
  48.                 f[i&1][j][k]=f[i&1][j-1][k];  
  49.                 if (g[j][k])  
  50.                 {  
  51.                     int x=s[(i-1)&1][j-1][k-1];  
  52.                     f[i&1][j][k]=(f[i&1][j][k]+s[(i-1)&1][j-1][k-1])%Mod;  
  53.                     int J=max(j-g[j][k]-1,0),K=max(k-g[j][k]-1,0);  
  54.                     int y=s[(i-1)&1][J][K];  
  55.                     f[i&1][j][k]=((f[i&1][j][k]-s[(i-1)&1][J][K])%Mod+Mod)%Mod;  
  56.                 }  
  57.                 s[i&1][j][k]=(s[i&1][j-1][k-1]+f[i&1][j][k])%Mod;         
  58.             }  
  59.     }  
  60.     printf("%d\n",f[p&1][n][m]);  
  61. }  




T3  第一眼觉得是倍增,然后各种写,越写越觉得太繁琐,好像实现不了。最后只写了一个链,时间复杂度还算错了,所以只有5分。其实还是有很多部分分的,但是考试时没有时间写了。
考完试思考了一下感觉还是做不大出来,听学长说了二分,不是很懂,下面的来自Rivendell的博客: http://www.cnblogs.com/Rivendell/p/4972055.html

总的来说,NOIP2015做的还是可以的,简单一点的题没有丢分,但是有一些思考复杂度高一点的题目没有做出来,下一步应该加强训练。

2016.10.18update 时至今日,懒惰的Po终于把自己过掉的T3贴出来。
详细题解 戳这里
撒花~~
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. using namespace std;  
  5. #define N 300005  
  6. #define sz 19  
  7.   
  8. int n,m,x,y,z,Max,dfs_clock,ans;  
  9. int tot,point[N],nxt[N*2],v[N*2],c[N*2];  
  10. int h[N],dis[N],val[N],num[N],tmp[N],f[N][sz+5];  
  11. struct hp{int x,y,lca,dis;}edge[N];  
  12.   
  13. void addedge(int x,int y,int z)  
  14. {  
  15.     ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;  
  16.     ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;  
  17. }  
  18. void build(int x,int fa)  
  19. {  
  20.     num[++dfs_clock]=x;  
  21.     for (int i=1;i<sz;++i)  
  22.     {  
  23.         if ((h[x]-(1<<i))<1) break;  
  24.         f[x][i]=f[f[x][i-1]][i-1];  
  25.     }  
  26.     for (int i=point[x];i;i=nxt[i])  
  27.         if (v[i]!=fa)  
  28.         {  
  29.             f[v[i]][0]=x;  
  30.             h[v[i]]=h[x]+1;dis[v[i]]=dis[x]+c[i];val[v[i]]=c[i];  
  31.             build(v[i],x);  
  32.         }  
  33. }  
  34. int lca(int x,int y)  
  35. {  
  36.     if (h[x]<h[y]) swap(x,y);  
  37.     int k=h[x]-h[y];  
  38.     for (int i=0;i<sz;++i)  
  39.         if ((1<<i)&k) x=f[x][i];  
  40.     if (x==y) return x;  
  41.     for (int i=sz-1;i>=0;--i)  
  42.         if (f[x][i]!=f[y][i])  
  43.             x=f[x][i],y=f[y][i];  
  44.     return f[x][0];  
  45. }  
  46. bool check(int mid)  
  47. {  
  48.     int cnt=0,limit=0;memset(tmp,0,sizeof(tmp));  
  49.     for (int i=1;i<=m;++i)  
  50.         if (edge[i].dis>mid)  
  51.         {  
  52.             ++tmp[edge[i].x];++tmp[edge[i].y];tmp[edge[i].lca]-=2;  
  53.             limit=max(limit,edge[i].dis-mid);  
  54.             cnt++;  
  55.         }  
  56.     if (!cnt) return true;  
  57.     for (int i=n;i>1;--i) tmp[f[num[i]][0]]+=tmp[num[i]];  
  58.     for (int i=2;i<=n;++i)  
  59.         if (val[i]>=limit&&tmp[i]==cnt) return true;  
  60.     return false;  
  61. }  
  62. int find()  
  63. {  
  64.     int l=0,r=Max,mid,ans;  
  65.     while (l<=r)  
  66.     {  
  67.         mid=(l+r)>>1;  
  68.         if (check(mid)) ans=mid,r=mid-1;  
  69.         else l=mid+1;  
  70.     }  
  71.     return ans;  
  72. }  
  73. int main()  
  74. {  
  75.     scanf("%d%d",&n,&m);  
  76.     for (int i=1;i<n;++i)  
  77.     {  
  78.         scanf("%d%d%d",&x,&y,&z);  
  79.         addedge(x,y,z);Max+=z;  
  80.     }  
  81.     build(1,0);  
  82.     for (int i=1;i<=m;++i)  
  83.     {  
  84.         scanf("%d%d",&edge[i].x,&edge[i].y);  
  85.         edge[i].lca=lca(edge[i].x,edge[i].y);  
  86.         edge[i].dis=dis[edge[i].x]+dis[edge[i].y]-dis[edge[i].lca]*2;  
  87.     }  
  88.     ans=find();  
  89.     printf("%d\n",ans);  
  90. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值