算法竞赛第七章

例题:Uva1601/埃及分数问题/Uva11212 待续
Uva1601/11212 基本都参考了网上的代码,感觉自己的思想还不够,代码写的还不够…


Uva1601 //这题基本是看的网上大佬的代码

大意是几个点从初始位置到结束位置,求最短的时间。最多三个点,2*2的格子里必有障碍物。
主要考建图!
看了书,对于把空格提出来建图十分不理解,以为标记一下就可以了,是我太天真….这一题可以用双向bfs写,待续吧。
感觉主要思想就是把空格提出来,将每个空格看作一个结点,找出空格附近有几个空格可用,记录编号。
再然后就是一个开心的bfs
感觉什么时候都要想一想怎么建图….

#include <bits/stdc++.h>

using namespace std;
#define maxn 300
int way[5][2] = {0,0,0,1,0,-1,1,0,-1,0};
int ukg[maxn];  //每个空格周围有多少个可以走的空格
int kg[maxn][5];//记录当前id的空格周围的可走空格
int st[3],sp[3];
int id[maxn][maxn];
int m,n,k;
bool vis[maxn][maxn][maxn];

int ok(int a,int b,int a1,int b1)   //不能互换位置和走到一个格子里
{
    if(a1 == b1) return 0;
    if(a1 == b && b1 == a) return 0;
    return 1;
}
int check(int a,int b,int c)
{
    if(a == sp[0] && b == sp[1] && c == sp[2] )return 1;
    else return 0;
}
struct node
{
    int a,b,c;
    int d;
    node(int x,int y,int z,int q = 0)
    {
        a = x; b = y; c = z;
        d = q;
    }
    node(){}
};

int bfs()
{
    memset(vis,0,sizeof(vis));
    queue<node> Q;
    Q.push(node(st[0],st[1],st[2]));
    vis[st[0]][st[1]][st[2]] = 1;
    while(!Q.empty())
    {
        node t = Q.front(); Q.pop();
        /*cout<<"d "<<t.d<<endl;
        cout<<t.a<<' '<<t.b<<' '<<t.c<<endl;
        cout<<endl;*/
        if(check(t.a,t.b,t.c))
        {
            printf("%d\n",t.d);
            break;
        }
        for(int i = 0; i < ukg[t.a]; i++)
        {
            int a1 = kg[t.a][i];
            for(int j = 0; j < ukg[t.b]; j++)
            {
                int b1 = kg[t.b][j];
                if(!ok(t.a,t.b,a1,b1)) continue;
                for(int k = 0; k < ukg[t.c]; k++)
                {
                    int c1 = kg[t.c][k];
                    if(!ok(t.a,t.c,a1,c1)) continue;
                    if(!ok(t.b,t.c,b1,c1)) continue;
                    if(!vis[a1][b1][c1])
                    {
                        vis[a1][b1][c1] = 1;
                        Q.push(node(a1,b1,c1,t.d+1));
                    }

                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d%d",&m,&n,&k) && m+n+k)
    {
        getchar();
        memset(id,-1,sizeof(id));
        char s[20];
        int cnt = 0;
        int x[maxn] = {0},y[maxn] = {0};
        for(int i = 0; i < n ; i++)
        {
            cin.getline(s,20);

            for(int j = 0; j < m; j++)
            {
                if(s[j] != '#')
                {
                    x[cnt] = i,y[cnt] = j,id[i][j] = cnt;
                    if(isupper(s[j]))   sp[s[j]- 'A'] = cnt;
                    else if(islower(s[j]))  st[s[j] - 'a'] = cnt;
                    cnt++;
                }
            }
        }
        for(int i = 0; i < cnt; i++)//找出每个空格周围可以走的
        {
            ukg[i] = 0;
            for(int j = 0; j < 5 ; j++)
            {
                int nx = x[i] + way[j][0],ny = y[i] + way[j][1];
                if(id[nx][ny] != -1)
                {
                    kg[i][ukg[i]++] = id[nx][ny];
                }
            }
        }
        if(k <= 2)//不满3个补齐 说起来我一直觉得是分情况。。按我的想法写代码会非常难看
        {
            ukg[cnt] = 1; kg[cnt][0] = cnt;sp[2] = cnt;st[2] = cnt++;
        }
        if(k <= 1)
        {
            ukg[cnt] = 1; kg[cnt][0] = cnt;sp[1] = cnt; st[1] = cnt++;
        }


       // cout<<sp[0]<<' '<<sp[1]<<' '<<sp[2]<<endl;
        bfs();
    }
    return 0;
}

埃及分数问题

给你两个数 b a 求b/a = 1/c1 + 1/c2 + 1/c3 …… 要求c1 c2 c3…… 不能重复,而且cn数量越小越好,最小的分数越大越好。
按照紫书的代码敲了一遍。
思想: 迭代加深搜索
即每次给定一个上界 maxd,每次只在这个层数内搜索是否有解。因为这种题目没有明显的上限,直接dfs会超时,而且找到答案也找了很多不必要的分支。
再往下 就是IDA*算法,讲道理才接触我觉得这一题我还想勉强想到限定上界,但是求这个乐观估价函数。。。自我感觉IDA*就是花式剪枝。

//埃及分数问题
#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define maxn 15
int ans[maxn];
int v[maxn];
int a,b;
int maxd = 1;
int gcd(int n, int m)
{
   return m ? gcd(m,n%m): n ;
}
int get_first(int aa,int bb)
{
    int c = 1;
    for(; c <= bb; c++)
    {
        int g = gcd(c,bb);
        int x = c/g*bb;
        if(x/c <= aa*x/bb)
            break;
    }
    return c;

}
int better(int d)
{
    for(int i = d; i >= 0; i-- )
    {
        if(v[i] != ans[i])
        {
            return ans[i] == -1 || v[i] < ans[i];
        }
        return false;
    }

}
int dfs(int d,int from,int aa,int bb)
{
    //cout<<from<<endl;
    if(d == maxd)
    {
        if(bb % aa) return 0;
        v[d] = bb/aa;
        if(better(d)) memcpy(ans,v,sizeof(v));
        return 1;
    }
    bool ok = 0;
    from = max(from,get_first(aa,bb));
    for(int i = from; ;i++)
    {
        if(bb*(maxd+1-d) <= i*aa)break;
        v[d] = i;
        int b2 = bb*i;
        int a2 = aa*i - bb;
        int g = gcd(a2,b2);
        if(dfs(d+1,i+1,a2/g,b2/g)) ok = true;
    }
    return ok;
}


int main()
{

    while(~scanf("%d%d",&a,&b))
    {
        int ok = 0;
        maxd = 1;

        for(; ; maxd ++)
        {
            memset(ans,-1,sizeof(ans));
            if(dfs(0, get_first(a,b),a,b))
                ok = 1;
            if(ok) break;
        }
        for(int i = 0; i <= maxd; i++)
            printf("%d ",ans[i]);
        printf("\n");
    }
    return 0;
}

uva11212

给出n个自然段,用复制粘贴的方法将其变成有序。
主要思想按紫书。
感觉这类题目最难的位置也就是思考如何剪枝,求乐观估价函数….限制层数。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define sint sizeof(int)
#define N 10

int data[N],moni[N],last[N];
int t,dep = 1;
int ok()
{
    for(int i = 0; i < t ; i++)
        if(moni[i] != i+1) return 0;
    return 1;
}
int hs()            //判断后续不正确的数字个数
{
    int sum = 0;
    for(int i = 1; i < t ; i++)
        if(moni[i] != moni[i-1]+1) sum++;
    return sum;
}
int cu(int i,int j, int k)
{
    int l[N],x = 0,num = j-i+1;
    memcpy(l,last,sizeof(last));
    memcpy(moni+k,last+i,sint*num);
    for(int a = i ; a+num < t ; a++)
        l[a] = l[a+num];

    for(int a = 0; a < k;a++)
        moni[a] = l[x++];
    for(int a = k+num; a < t; a++)
        moni[a] = l[x++];
    return 1;
}
int dfs(int n)
{

    if(n == dep)
        return ok();
    else
    {
        if( hs() + n*3 > dep*3) return false;
        int old[N];
        memcpy(old,moni,sizeof(moni));
        memcpy(last,old,sizeof(last));
        for(int i = 0; i < t ; i++)
            for(int j = i; j < t; j++)
                for(int k = 0; k < t-(j-i); k++)
                {
                    if(i == k) continue;
                    cu(i,j,k);
                    if(dfs(n+1) == 1) return true;
                    memcpy(moni,old,sizeof(old));
                    memcpy(last,old,sizeof(old));
                }
        return false;
    }

}
int main()
{
    int num = 1;
//    freopen("D://in.txt","r",stdin);
    while(~scanf("%d",&t) && t)
    {
        for(int i = 0; i < t ; i++)
            scanf("%d",&data[i]);
        dep = 0;
        memcpy(moni,data,sizeof(data));
        int ok = 0;
        int cnt = hs();
        ok = cnt == 0;
        if(!ok)
        {
            for(; dep <= t-1; dep++)

            if(dfs(0) == 1) break;
        }
        printf("Case %d: %d\n",num++,ok ? 0 : dep);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值