171129课后作业

A题是八数码,每次交换x和旁边的数字,求怎么交换能最后变成1 2 3 4 5 6 7 8 x的形式

用正常的BFS搜索+康托展开判重

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#include <string.h>
using namespace std;
int vis[400000], a[3][3];
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
char dir[] = {'r','d','l','u'};
int ed[3][3], flag1 = 0, x_x, y_x;
long long fac[]= {1,1,2,6,24,120,720,5040,40320,362880}; //阶乘表
//康托展开
long long cantor(int a[][3])
{
    long long temp, num = 0;
    int i, j;
    for(i = 0; i < 9; i++)
    {
        temp = 0;
        for(j = i + 1; j < 9; j++)
        {
            if(a[j / 3][j % 3] < a[i / 3][i % 3]) temp++;
            //判断几个数小于它
        }
        num += fac[8 - i] * temp;
    }
    return num;
}
long long cal(int a[][3])
{
    long long res;
    res = a[0][0] * 1e8 + a[0][1] * 1e7 + a[0][2] * 1e6 + a[1][0] * 1e5 + a[1][1] * 1e4 + a[1][2] * 1e3 + a[2][0] * 1e2 + a[2][1] * 10 + a[2][2];
    return res;
}
struct node
{
    int s[3][3];
    int ans;
    int step;
    char path[111];
}now, next;
queue<node> q;
int check(int s[][3])
{
    int i, j;
    int flag = 0;
    for(i = 0; i < 3; i++)
    {
        for(j = 0; j < 3; j++)
        {
            if(now.s[i][j] != ed[i][j]) flag = 1;
        }
    }
    return flag;
}
void bfs(int a[][3])
{
    while(!q.empty()) q.pop();
    int i, j, xx = x_x, yy = y_x;
    for(i = 0; i < 3; i++)
    {
        for(j = 0; j < 3; j++)
        {
            now.s[i][j] = a[i][j];
        }
    }
    now.ans = cantor(a);
    vis[now.ans] = 1;
    now.step = -1;
    q.push(now);
    while(!q.empty())
    {
        now = q.front();
        q.pop();
        if(now.ans == 46233)
        {
            puts(now.path);
            return;
        }
        for(i = 0; i < 3; i++)
        {
            for(j = 0; j < 3; j++)
            {
                if(now.s[i][j] == 0) {xx = i; yy = j;}
            }
        }
        //记录此时"x"的位置
        for(i = 0; i < 4; i++)
        {
            //四个方向拓展
            int x1, y1;
            x1 = xx + dx[i];
            y1 = yy + dy[i];
            if(x1 < 0 || x1 > 2 || y1 < 0 || y1 > 2) continue;
            next = now;
            next.s[xx][yy] = now.s[x1][y1];
            next.s[x1][y1] = 0;
            next.ans = cantor(next.s);
            next.step = now.step + 1;
            next.path[next.step] = dir[i];
            if(vis[next.ans])
            {
                continue;
                //康托展开判重
            }
            if(next.ans == 46233)
            {
                puts(next.path);
                return;
            }
            vis[next.ans] = 1;
            q.push(next);
        }
    }
    printf("unsolvable\n");
}
int main()
{
    char s[2], i;
    int xx, yy;
    ed[0][0] = 1;
    ed[0][1] = 2;
    ed[0][2] = 3;
    ed[1][0] = 4;
    ed[1][1] = 5;
    ed[1][2] = 6;
    ed[2][0] = 7;
    ed[2][1] = 8;
    ed[2][2] = 0;
    for(i = 0; i < 9; i++)
    {
        scanf("%s", s);
        int x;
        if(s[0] == 'x') {x = 0; x_x = i / 3; y_x = i % 3;}
        else x = s[0] - '0';
        xx = i / 3;
        yy = i % 3;
        a[xx][yy] = x;
    }
    memset(vis, 0, sizeof(vis));
    bfs(a);
    return 0;
}
//2  3  4  1  5  x  7  6  8
//1 2 3 4 x 6 7 5 8
/*
2 3 4
1 5 x
7 6 8
*/

B题是一个BFS,在一个矩阵里走日字,从起点到终点最少多少步

BFS的模板还是很万能的

这里注意一下输入,“e1 e2”要分两个输,给出的两种都是可以的,但是不能用while(*gets(s)!=EOF)  *gets(s)表示s[0],而gets读到EOF的时候返回的是NULL,用*取NULL里的值就RE了while(gets(s)!=NULL)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <queue>
using namespace std;
struct kkk
{
    int x;
    int y;
    int step;
};
int dx[] = {2, 2, 1, 1, -1, -1, -2, -2};
int dy[] = {1, -1, 2, -2, 2, -2, 1, -1};
//走日字的方向
int vis[100][100];
queue<kkk> q;
bool check(int x, int y)
{
    if(x < 1 || y < 1 || x > 8 || y > 8) return 0;
    else if(vis[x][y] == 1) return 0;
    else return 1;
}
int bfs(int a, int b, int aa, int bb)
{
    while(!q.empty()) q.pop();
    kkk now, next;
    now.x = a;
    now.y = b;
    now.step = 0;
    q.push(now);
    if(now.x == aa && now.y == bb) return 0;
    memset(vis, 0, sizeof(vis));
    vis[now.x][now.y] = 1;
    while(!q.empty())
    {
        now = q.front(); q.pop();
        int i;
        for(i = 0; i < 8; i++)
        {
            next.x = now.x + dx[i];
            next.y = now.y + dy[i];
            if(check(next.x, next.y))
            {
                vis[next.x][next.y] = 1;
                next.step = now.step + 1;
                q.push(next);
                if(next.x == aa && next.y == bb) return next.step;
            }
        }
    }
    return next.step;
}
int main()
{
    char s[10], c1, c2;
    int a1, a2;
//    while(~scanf(" %c%d %c%d", &c1, &a1, &c2, &a2))//前面有一个空格 嗯
//    {
//        int sx = c1 - 96;
//        int ex = c2 - 96;
//        int res;
//        res = bfs(sx, a1, ex, a2);
//        printf("To get from %c%d to %c%d takes %d knight moves.\n", c1, a1, c2, a2, res);
//    }
    while(~scanf("%s%s",s,s+3))
    {
        int sx = s[0] - 96, sy = s[1] - '0';
        int ex = s[3] - 96, ey = s[4] - '0';
        int res;
        res = bfs(sx, sy, ex, ey);
        printf("To get from %c%c to %c%c takes %d knight moves.\n", s[0], s[1], s[3], s[4], res);
    }

    return 0;
}


C题是一个LIS,寻找前一位递增后一位递减的最长序列

On2的,用一个pre数组记录路径,这个数组就相当于一个指针,然后最后逆序输出

也有一点点模板的影子

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
struct kkk
{
    int w;
    int s;
    int num;
} m[1005];
bool cmp(kkk a, kkk b)
{
    if(a.w != b.w) return a.w < b.w;
    return a.s > b.s;
}
using namespace std;
int f[1005], pre[1005], res[1005];
int main()
{
    int n = 1;
    int i, j, ans = 0, p = 0;
    while(~scanf("%d %d", &m[n].w, &m[n].s))
    {
        n++;
        m[n].num = n;
        f[n] = 1;
    }
    n--;//这么记录n会多一个,虽然没n--也ac了……
    sort(m + 1, m + 1 + n, cmp);
    //printf("%d\n", n);
    for(i = 1; i <= n; i++)
    {
        for(j = 1; j <= i; j++)
        {
            if(m[j].w < m[i].w && m[j].s > m[i].s)
            {
                if(f[i] < f[j] + 1)
                {
                    f[i] = f[j] + 1;
                    pre[i] = j;//记录转移路径
                }
            }
        }
        if(f[i] > ans)
        {
            ans = f[i];
            p = i;
        }
    }
    printf("%d\n", ans);
    i = p;
    j = 0;
    while(i > 0)
    {
        res[j] = m[i].num;
        j++;
        i = pre[i];
    }
    for(i = j - 1; i >= 0; i--)
    {
        printf("%d\n", res[i]);
    }


    return 0;
}


D是一个要 回溯的DFS,做完i题做j题需要arr[i][j]时间,做题的时间要求递增(可以相等),问最多做多少道题

xxx【这是dfs之前做的事情】
dfs();【递归】
xxx【这是一层dfs做完后回溯做的事情】

wa了很久很久,发现dfs的起始状态写错了……

#include <stdio.h>
#include <stdlib.h>
/*
做完i题做j题需要arr[i][j]时间,做题的时间要求递增(可以相等),问最多做多少道题
这是一道DFS题
用到了回溯
xxx【这是dfs之前做的事情】
dfs();【递归】
xxx【这是一层dfs做完后回溯做的事情】
*/
/*
当时wa了好久,第一次递归dfs(0, 0, 1);
第一个0不是arr[0][0],是第一个做第0题==
*/
int vis[20], arr[18][18];
int n, res = 1;
int check(int last, int now, int t)
{
    if(arr[last][now] < t || vis[now] == 1) return 0;
    else return 1;
}
/*last->上一个做的第last题
t->做上一个题用了t时间
ans->现在总共做了ans个题
*/
void dfs(int last, int t,int ans)
{
    int now;
    vis[last] = 1;
    for(now = 1; now < n; now++)
    {
        if(check(last, now, t))
        {
            vis[now] = 1;
            ans++;
            if(ans > res) res = ans;
            dfs(now, arr[last][now], ans);
            vis[now] = 0;
            ans--;
            //一层递归做完 回溯
        }
    }

}
int main()
{
    while(~scanf("%d", &n))
    {
        int i, j;
        res = 1;
        for(i = 0; i < n; i++)
        {
            for(j = 0; j < n; j++)
            {
                scanf("%d", &arr[i][j]);
            }
            vis[i] = 0;
        }
        dfs(0, 0, 1);
        printf("%d\n", res);
    }
    return 0;
}


E是一个归并求逆序数,给一列数求在冒泡排序一遍的过程中每个数能达到的最左端和最右端的差

最左端就是它起始位置和终止位置(就是这个数的大小)的min

最右端就是它的起始位置加上它右面有多少个数比它小(比它小的数在每次冒泡的时候要左移,相应这个数就要往右移)

数据到1e5,所以要用归并,复杂度为nlogn

归并带递归的,大佬给讲了很久才听懂==

归并就是把大区间一次一次分成两半,分到最后每一半都只有一个之后开始归并,两段归并排好序之后合起来,再和下一段归并,这样归并的区间越来越长,最后就把整个区间处理好了

归并的最初目的其实是排序,但是后来发现在归并过程中可以统计一些信息,就比如这道题的逆序数

归并的过程还要好好理解一下

小注:warning“Implicit declaration of function”“conflicting types for merge”,把merge函数写在了后面,sort函数里出现了merge函数,就要在sort前面写merge

#include <stdio.h>
#include <stdlib.h>
int a[100005], l[100005], r[100005], cnt[100005], tmp[100005], num[100005];
int k;
int min(int a, int b)
{
    return b>a?a:b;
}
void merge(int first, int mid, int last)
{
    int i, j;
    if(last - first < 1) return;
    i = first, j = mid + 1;
    k = 1;
    //cnt 前面有多少比它大的
    while(i <= mid && j <= last)
    {
        if(a[i] < a[j])
        {
            tmp[k] = a[i];
            k++;
            i++;
        }
        else //a[i] > a[j]
        {
            //cnt[a[j]] += mid - first + 1 - i + 1;
            cnt[a[j]] += mid + 1 - i;
            tmp[k] = a[j];
            k++;
            j++;
        }
    }
    while(i <= mid)
    {
        tmp[k] = a[i];
        k++;
        i++;
    }
    while(j <= last)
    {
        tmp[k] = a[j];
        k++;
        j++;
    }
    for(i = 1; i <= last - first + 1; i++)
    {
        a[first + i - 1] = tmp[i];
        //printf("%d ", tmp[i]);
    }
    //printf("\n");
}
void sort(int first, int last)
{
    if(first < last)
    {
        int mid = (first + last) / 2;
        sort(first, mid);
        sort(mid + 1, last);
        merge(first, mid, last);
    }
}

int main()
{
    int T, t;
    scanf("%d", &T);
    for(t = 1; t <= T; t++)
    {
        int n, i;
        k = 1;
        scanf("%d", &n);
        for(i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            l[a[i]] = min(a[i], i);
            cnt[i] = 0;
            num[a[i]] = i;
        }
        sort(1, n);

        printf("Case #%d:", t);
        for(i = 1; i <= n; i++)
        {
            //cnt[i] = i - 1 - (num[i] - 1 - cnt[i]);
            //cnt[i] = i - num[i] + cnt[i];
            //r[i] = num[i] + cnt[i];
            r[i] = i + cnt[i];
            //printf("%d %d %d\n", r[i], l[i], cnt[i]);
            printf(" %d", r[i] - l[i]);
        }
        printf("\n");
    }
    return 0;
}

F就很水了,字符串处理,分类讨论

注意当出现“0/0”的时候会输出奇怪的东西

太简单就不粘代码了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值