紫书第4章 函数和递归(部分题)

前言吐槽:

不得不说 我太弱 Or 紫书或者太难,肯定是前者。又一次开始刷,选择从第四章开始,不知道是不是个错误…模拟题真的好麻烦,还有纯考智商的题目都写不动,何谈刷。不如当初切搜索水题有快感。

例题:

部分代码都存在坏掉电脑里和还没做的题目,有机会再补上。

4-1 UVa1339–Ancient Cipher

题目给的思路很清晰,字母可以随意重排,所以和前后位置没有关系,只需要分别统计每个字母出现的次数放在数组里,然后排序,由于是一一对应关系,再将排的序后的数组进行比较,相等说明可一一对应上。注意,这里本想用memcmp,发现不管用。

4-2 UVa489–Hangman Judge

这题实现不难,主要学会如何用优雅的代码(短小,且易懂)水过。几个变量的取名,子函数的写法等,

4-3 UVa133–The Dole Queue

这题,也是经典模拟题,我直接看的书,,,绕圈取余就好,一个go函数就实现两个有共性的操作,最优代码,开始还能纳闷p1明明从1开始,怎么带入是n,后来推敲原来这里他每次从出发点到下一点算走两次,就是到出发点也算一次。

[cpp]
#include<cstdio>
#include<cstring>
using namespace std;
int a[25],n,k,m;

int go(int p,int d,int t)
{
   while(t--)
   {
       do{p=(p-1+d+n)%n+1;} while(a[p]==0);
   }
   return p;
}
int main()
{
    while(~scanf("%d %d %d",&n,&k,&m)&&n&&k&&m)
    {
        for(int i=1;i<=n;i++) a[i]=i;
        int left=n,p1=n,p2=1;
        while(left)
        {
            p1=go(p1,1,k);
            p2=go(p2,-1,m);
            printf("%3d",p1);left--;
            if(p2!=p1) {printf("%3d",p2);left--;}
            a[p1]=a[p2]=0;
            if(left) printf(",");
        }
        printf("\n");
    }
    return 0;
4-4 UVa213–Message Decoding

这题 也是琢么了好久,数据读入到实现算法当初完全一头雾水…..最坑爹是这题照书上代码会W,看了大牛题解,实现也有点问题,交了竟AC(渣渣 求别笑)考点 三个,换行的读入, 二进制转十进制,还有移位实现乘二。

#include <stdio.h>
#include <string.h>
char code[8][1<<8], s[1<<8];

// 过滤空行,返回字符串长度
int my_gets(char* s) {
    int k;
    while ((k = getchar()) == '\n');  // 空行重读
    if (k == -1) k = 0; else gets(s+1);
    s[0] = k; return strlen(s);
}

void init() {
    int i, j, k = 0;
    for (i = 1; ; i++) {
        for (j = 0; j < (1<<i)-1; j++) {
            code[i][j] = s[k++];
            if (!s[k]) return;
        }
    }
}

// 读取01字符,返回0、1整型值
int read() {
    char ch;
    while ((ch = getchar()) == '\n');
    return ch - '0';
}

// 读取c个01字符
int readint(int c) {
    int v = 0;
    while (c--) v = (v<<1)+read();
    return v;
}

int main() {
    int len, v;
    while (my_gets(s)) {
        init();
        while (len = readint(3))
            while ((v = readint(len)) != ((1<<len)-1))
                putchar(code[len][v]);
        putchar('\n');
    }
    return 0;
}
4-5 UVa512–Spreadsheet Tracking

这题纯粹的模拟题,自己写的时候,思路神马都对,交了w,之后看书上,还有更好思路的做法,就是先将所有操作保存,然后对每个查询重新执行每个操作对于关注的单元格,而不是对所有的电子表格进行操作。对于题目给定的规模来说,这个方法不仅更好写,而且效率更高。

#include<cstdio>
#include<cstring>
#define maxn 10000
struct command
{
    char c[5];
    int r1,r2,c1,c2;
    int a,x[20];
}cmd[maxn];
int r,c,n;
int simulate(int &r0,int &c0)
{
    for(int i=0;i<n;i++)
    {
        if(cmd[i].c[0]=='E')
        {
            if(r0==cmd[i].r1&&c0==cmd[i].c1) {r0=cmd[i].r2,c0=cmd[i].c2;}
            else if(r0==cmd[i].r2&&c0==cmd[i].c2){r0=cmd[i].r1,c0=cmd[i].c1;}
        }
        else
        {
            int dr=0,dc=0;
            for(int j=0;j<cmd[i].a;j++)
            {
                int X=cmd[i].x[j];
                if(cmd[i].c[0]=='D')
                {
                    if(cmd[i].c[1]=='R'&&X==r0) return 0;
                    if(cmd[i].c[1]=='C'&&X==c0) return 0;
                    if(cmd[i].c[1]=='R'&&X<r0) dr--;
                    if(cmd[i].c[1]=='C'&&X<c0) dc--;
                }
                else
                {
                    if(cmd[i].c[1]=='R'&&X<=r0) dr++;
                    if(cmd[i].c[1]=='C'&&X<=c0) dc++;
                }
            }
            r0+=dr,c0+=dc;
        }
    }
    return 1;
}

int main()
{
    int kcase=0;
    while(scanf("%d %d",&r,&c)&&r&&c)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",cmd[i].c);
            if(cmd[i].c[0]=='E')
            {
                scanf("%d%d%d%d",&cmd[i].r1,&cmd[i].c1,&cmd[i].r2,&cmd[i].c2);
            }
            else
            {
                scanf("%d",&cmd[i].a);
                for(int j=0;j<cmd[i].a;j++)
                scanf("%d",&cmd[i].x[j]);
            }
        }
        if(kcase>0) printf("\n");
        printf("Spreadsheet #%d\n",++kcase);
        int cnt;
        scanf("%d",&cnt);
        while(cnt--)
        {
            int a,b;scanf("%d%d",&a,&b);
            printf("Cell data in (%d,%d) ",a,b);
            if(!simulate(a,b)) printf("GONE\n");
            else printf("moved to (%d,%d)\n",a,b);
        }
    }
}

4-6 跳过

习题:

4-2 UVa201–Squares

这题 LRJ大神 翻译的有点瑕疵…Hij代表行,Vij代表列,而V中ij是反过来的,注意读入反过来就好。
题意,告诉你哪些地方有边,然后数构成大小从小打到的正方形分别多少个。
算法就是,从第一个位置作为正方形的左上角开始扫,几个循环控制,最外面是正方形的边长,再是位置的两个循环,再是扫几个单位的循环,思路非常清晰。

#include<cstdio>
#include<cstring>
using namespace std;
int v[15][15],h[15][15],n,m;
int main()
{
    int t=0;
    while(~scanf("%d %d",&n,&m))
    {
        getchar();
        memset(v,0,sizeof(v));
        memset(h,0,sizeof(h));
        for(int i=1;i<=m;i++)
        {
            char c;int a,b;
            scanf("%c %d %d",&c,&a,&b);
            getchar();
            if(c=='V') v[b][a]=1;
            else if(c=='H') h[a][b]=1;
        }
        if(t>0)    printf("\n**********************************\n\n");
        printf("Problem #%d\n\n",++t);
        int l,flag,cnt,sum=0;
        for(l=1;l<=n;l++)
        {
            cnt=0;
            for(int i=1;i+l<=n;i++)
            {
                for(int j=1;j+l<=n;j++)
                {
                    flag=1;
                    for(int k=j;k<j+l;k++)
                      if(!h[i][k]||!h[i+l][k]) flag=0;
                    for(int k=i;k<i+l;k++)
                      if(!v[k][j]||!v[k][j+l]) flag=0;
                    cnt+=flag;
                }
                sum+=cnt;
            }
            if(cnt)
              printf("%d square (s) of size %d\n",cnt,l);
        }
        if(!sum) printf("No completed squares can be found.\n");
    }
}
UVa253–Cube painting

据说这题应该,枚举24种可能,六个可能做顶面,四个可能做正面的搞,还有模拟旋转的搞法,然而智商做鸡没想纠结,所以我是判断三个对边相等就符合相同正方形。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char s1[10],s2[10];
int vis[6];
int compare(int x,int y,int a,int b,int c,int d)
{
    if(!vis[x]&&!vis[y]&&((s1[x]==s2[x]&&s1[y]==s2[y])||(s1[x]==s2[y]&&s1[y]==s2[x])))
    {
        vis[x]=vis[y]=1;
        return 1;
    }
    else if(!vis[a]&&!vis[b]&&((s1[x]==s2[a]&&s1[y]==s2[b])||(s1[x]==s2[b]&&s1[y]==s2[a])))
    {
        vis[a]=vis[b]=1;
        return 1;
    }
    else if(!vis[c]&&!vis[d]&&((s1[x]==s2[c]&&s1[y]==s2[d])||(s1[x]==s2[d]&&s1[y]==s2[c])))
    {
        vis[c]=vis[d]=1;
        return 1;
    }
    else
      return 0;
}

int main()
{
    while(~scanf("%6s%6s",s1,s2))
    {
         memset(vis,0,sizeof(vis));
         int cnt=0;
         cnt+=compare(0,5,1,4,2,3);
         cnt+=compare(1,4,0,5,2,3);
         cnt+=compare(2,3,1,4,0,5);
         if(cnt==3) printf("TRUE\n");
         else printf("FALSE\n");
    }
    return 0;
}
4-10 UVa815–Flooded!

这题二分被我写得感人,精度问题一直w,快要放弃之前,人品爆发随意加两0过了….教训思路没有明显错误的情况下,二分精度检查是首步。
再说这题,LRJ把这题放在最后一题,明显与难度不符…我觉得前面模拟难写多了….这题把所有格子高度排个序,二分水能淹过的高度。
当然还有别的思路,排序后从第一个最小的开始淹漠,这里也有两种写法。详细就不说了,大神题解里都有。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define eps 1e-10
using namespace std;
double M[100000],sum;
int n,m;
int judge(double c)
{
    double s=0;
      for(int i=0;i<m*n;i++)
    {
         if(c>=M[i])
            s+=c-M[i];
    }
    if(s>=sum) return 1;
    else return 0;
}

int main()
{
    int cnt=0;
    while(~scanf("%d %d",&m,&n)&&m&&n)
    {
        for(int i=0;i<m*n;i++)
            scanf("%lf",&M[i]);
        sort(M,M+m*n);
        scanf("%lf",&sum);
        sum/=100;
        double l=M[0],r=1000.0;
        while(l+eps<=r)
        {
            double mid=(l+r)*0.5;
            if(judge(mid))
                r=mid;
            else
                l=mid+0.0001;//开始是0.01,0.001 都没过,以后写二分一定注意精度问题,一般比题目结果小数后面多两个0。
        }
        double ans=0;
        for(int i=0;i<n*m;i++)
            if(M[i]<=r) ans++;
        printf("Region %d\n",++cnt);
        printf("Water level is %.2lf meters.\n",r);
        printf("%.2lf percent of the region is under water.\n",ans*100.0/(double)(n*m));
        printf("\n");
    }
    return 0;
}

第四章,切过水题,告一段落,开始向第五章和动规进发!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值