埃及分数&&The Rotation Game&&骑士精神——IDA*

IDA*:非常好用的搜索,可以解决很多深度浅,但是规模大的搜索问题。

估价函数设计思路:观察一步最多能向答案靠近多少。

埃及分数

题目大意:

  给出一个分数,由分子a 和分母b 构成,现在要你分解成一系列互不相同的单位分数(形如:1/a,即分子为1),要求:分解成的单位分数数量越少越好,如果数量一样,最小的那个单位分数越大越好。

如:

  19/45 = 1/3 + 1/12 + 1/180;

  19/45 = 1/5 + 1/6 + 1/18;

  以上两种分解方法都要3个单位分数,但下面一个的最小单位分数1/18比上一个1/180大,所以第二个更优。

题解:
dfs直接搜爆炸,因为深度无限。

bfs爆炸,空间不行。

所以,采用有深度限制的,并且不耗费空间的迭代加深搜索。

深度即分数的个数。

配合一下A*思想。

剪枝:

1.每次一步到达小于剩余分数的最小分母。

return b/a+1

2.如果当前可以选择的分数(已经是最大的)*剩下的步数<剩下的分数,return (A*思想)

3.如果只剩最后一步,直接判断能否取到。

对于不能取的处理,开一个数组记录。>1000都可以取的。

分数运算,手推式子。注意约分

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000+5;
ll a,b;
int k,T;
bool fl;
ll ans[N];
ll mem[N];
bool no[N];
int dp;
ll getfirst(ll a,ll b){
    return b/a+1;
}
bool cmp1(ll s1,ll m1,ll s2,ll m2){//s1/m1 > s2/m2 ?
    return (ll)s1*m2>(ll)m1*s2;
}
bool cmp2(ll s1,ll m1,ll s2,ll m2){//s1/m1 >= s2/m2 ?
    return (ll)s1*m2>=(ll)m1*s2;
}
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
void sub(ll &s1,ll &m1,ll s2,ll m2){
    s1=s1*m2-s2*m1;
    m1=m1*m2;
    ll g=gcd(s1,m1);
    s1/=g;
    m1/=g;
}
bool better(){
    for(int i=dp;i>=dp;i--){
        if(mem[i]!=ans[i]){
            //cout<<mem[i]<<" "<<ans[i]<<endl;
            return (ans[i]==0)||(mem[i]<ans[i]);
        }
    }
    return false;
}
void dfs(int now,ll lim,ll rs,ll rm){
    //if(dp<4) cout<<now<<" "<<lim<<" "<<rs<<" "<<rm<<endl;
    if(now==dp){
        if(rs==1){
            if(rm<lim) return;
            //if(rm<=1000&&no[rm]) return;
            fl=true;
            //cout<<" ok "<<endl;
            mem[dp]=rm;
            if(better()){
            ///cout<<"better "<<endl;
                for(int i=1;i<=dp;i++){
                    //cout<<mem[i]<<" ";
                    ans[i]=mem[i];
                }//cout<<endl;
            }
            mem[dp]=0;
        }
        return;
    }
    lim=max(lim,getfirst(rs,rm));
    for(int i=lim;;i++){
        if(cmp1(rs,rm,(dp-now+1),i)) return;
            if(cmp2(rs,rm,1,i)){
                ll hs=rs,hm=rm;
                sub(hs,hm,1,i);
                mem[now]=i;
                dfs(now+1,i+1,hs,hm);
                mem[now]=0;
            }
    }
}
int main(){
        scanf("%lld%lld",&a,&b);
        int t;
        for(int i=1;i<=k;i++){
            scanf("%d",&t);
            no[t]=1;
        }
        fl=false;
        while(!fl){
            dp++;
            //if(dp<4) cout<<dp<<endl;
            dfs(1,2,(ll)a,(ll)b);
        }
        for(int i=1;i<=dp;i++){
            printf("%lld ",ans[i]);
        }
    return 0;
}

The Rotation Game

每次8种选择吃不消。

迭代加深直接做。

A*估价:中间8个数,最多的那一个一次转动最多多一个。如果中间的8个最多的那一个和8的差距比剩余步数多,return

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=9;
int mp[N][N];
int st[N][N];
char sta[10005],top;
char ans[10005],sum;
int dp,num;
bool fl;
int cnt[4];
int fan[9]={0,6,5,8,7,2,1,4,3};
int fin(){
    cnt[1]=cnt[2]=cnt[3]=0;
    for(int i=3;i<=5;i++)
        for(int j=3;j<=5;j++)
            cnt[mp[i][j]]++;
    if(cnt[1]==8) return 1;
    if(cnt[2]==8) return 2;
    if(cnt[3]==8) return 3;
}
int che(){
    cnt[1]=cnt[2]=cnt[3]=0;
    for(int i=3;i<=5;i++){
        for(int j=3;j<=5;j++){
            cnt[mp[i][j]]++;
        }
    }
    int big=max(cnt[1],max(cnt[2],cnt[3]));
    return 8-big;
}
void mvh(int d){
    if(d==3){
        int tmp=mp[3][7];
        for(int i=7;i>=2;i--) mp[3][i]=mp[3][i-1];
        mp[3][1]=tmp;
    }
    else if(d==4){
        int tmp=mp[5][7];
        for(int i=7;i>=2;i--) mp[5][i]=mp[5][i-1];
        mp[5][1]=tmp;
    }
    else if(d==7){
        int tmp=mp[5][1];
        for(int i=1;i<=6;i++) mp[5][i]=mp[5][i+1];
        mp[5][7]=tmp;
    }
    else{
        int tmp=mp[3][1];
        for(int i=1;i<=6;i++) mp[3][i]=mp[3][i+1];
        mp[3][7]=tmp;
    }
}
void mvz(int d){
    if(d==1){
        int tmp=mp[1][3];
        for(int i=1;i<=6;i++) mp[i][3]=mp[i+1][3];
        mp[7][3]=tmp;
    }
    else if(d==2){
        int tmp=mp[1][5];
        for(int i=1;i<=6;i++) mp[i][5]=mp[i+1][5];
        mp[7][5]=tmp;
    }
    else if(d==5){
        int tmp=mp[7][5];
        for(int i=7;i>=2;i--) mp[i][5]=mp[i-1][5];
        mp[1][5]=tmp;
    }
    else{
        int tmp=mp[7][3];
        for(int i=7;i>=2;i--) mp[i][3]=mp[i-1][3];
        mp[1][3]=tmp;
    }
}
bool cmp(){
    for(int i=1;i<=dp;i++){
        if(sta[i]<ans[i]) return true;
        if(sta[i]>ans[i]) return false;
    }
    return false;
}
void dfs(int now,int las){
    if(che()>dp-now+1) return;
    if(now==dp+1){
        if(che()==0){
            if(!fl){
                memcpy(ans,sta,sizeof sta);
                num=fin();
            }
            else if(cmp()){
                memcpy(ans,sta,sizeof sta);
                num=fin();
            }
            fl=true;
        }
        return;
    }
    for(int i=1;i<=8;i++){
        if(i==fan[las]) continue;
        if((i-1)%4>1) mvh(i);
        else mvz(i);
        sta[++top]=i-1+‘A‘;
        dfs(now+1,i);
        sta[top--]=‘ ‘;
        if((fan[i]-1)%4>1) mvh(fan[i]);
        else mvz(fan[i]);
    }
}
void clear(){
    dp=0;
    fl=false;
}
int main(){
    while(1){
        clear();
        scanf("%d",&st[1][3]);
        //cout<<"aa "<<endl;
        if(st[1][3]==0) break;
        scanf("%d",&st[1][5]);
        scanf("%d%d",&st[2][3],&st[2][5]);
        for(int i=1;i<=7;i++) scanf("%d",&st[3][i]);
        scanf("%d%d",&st[4][3],&st[4][5]);
        for(int i=1;i<=7;i++) scanf("%d",&st[5][i]);
        scanf("%d%d",&st[6][3],&st[6][5]);
        scanf("%d%d",&st[7][3],&st[7][5]);
        memcpy(mp,st,sizeof st);
        if(che()==0){
            printf("No moves needed\n");
            printf("%d\n",fin());
            continue;
        }
        fl=false;
        while(!fl){
            dp++;
            memcpy(mp,st,sizeof st);
            dfs(1,0);
        }
        printf("%s\n",ans+1);
        printf("%d\n",num);
    }
    return 0;
}

骑士精神

也许可以折半爆搜。

但是显然不够漂亮。

明显“超15步-1”,就迭代加深了。

估价函数:每次,骑士最多归位一个。如果算上最后一步的空格,可能归位2个。

所以,当前和终止的差距-1大于剩余步数的话,一定不行。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=6;
int T;
char st[6][6];
int dp;
bool fl;
int nd[6][6]={
    {0,0,0,0,0,0},
    {0,1,1,1,1,1},
    {0,0,1,1,1,1},
    {0,0,0,2,1,1},
    {0,0,0,0,0,1},
    {0,0,0,0,0,0}
};
int mp[6][6];
int mv[8][2]={{-1,+2},{-1,-2},{-2,+1},{-2,-1},{+1,+2},{+1,-2},{+2,-1},{+2,+1}};
int che(int re){
    int dif=0;
    for(int i=1;i<=5;i++){
        for(int j=1;j<=5;j++){
            dif+=(mp[i][j]!=nd[i][j]);
        }
    }
    if(dif==0) return 2;
    return dp-re+1>=dif-1;
}
void dfs(int now,int x,int y){
    if(!che(now)) return;
    if(fl) return;
    if(now==dp+1){
        if(che(now)==2) {
            fl=true;return;
        }
    }
    for(int i=0;i<8;i++){
        int dx=x+mv[i][0],dy=y+mv[i][1];
        if(dx<1||dx>5) continue;
        if(dy<1||dy>5) continue;
        swap(mp[x][y],mp[dx][dy]);
        dfs(now+1,dx,dy);
        swap(mp[x][y],mp[dx][dy]);
    }
}
void clear(){
    fl=false;dp=0;
}
int main(){
    scanf("%d",&T);
    while(T--){
        clear();
        int sx,sy;
        for(int i=1;i<=5;i++){
            scanf("%s",st[i]+1);
            for(int j=1;j<=5;j++){
                if(isdigit(st[i][j]))mp[i][j]=st[i][j]-‘0‘;
                else {
                    mp[i][j]=2;sx=i,sy=j;
                }
            }
        }
        fl=false;
        for(dp=0;dp<=15;dp++){
            dfs(1,sx,sy);
            if(fl) break;
        }
        if(fl) printf("%d\n",dp);
        else printf("-1\n");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Miracevin/p/9780322.html

时间: 2024-11-01 00:44:22

埃及分数&&The Rotation Game&&骑士精神——IDA*的相关文章

[BZOJ 1085] [SCOI2005] 骑士精神 [ IDA* 搜索 ]

题目链接 : BZOJ 1085 题目分析 : 本题中可能的状态会有 (2^24) * 25 种状态,需要使用优秀的搜索方式和一些优化技巧. 我使用的是 IDA* 搜索,从小到大枚举步数,每次 DFS 验证在当前枚举的步数之内能否到达目标状态. 如果不能到达,就枚举下一个步数,重新搜索,即使某些状态在之前的 DFS 中已经搜索过,我们仍然搜索. 并且在一次 DFS 中,我们不需要判定重复的状态. 在 IDA* 中,重要的剪枝优化是 估价函数 ,将一些不可能存在可行解的枝条剪掉. 如果估价函数写得

[BZOJ 1085][SCOI 2005]骑士精神(IDA*搜索)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1085 考虑到深度不超过15,IDA*搜索可做. 估价函数h()=当前不在目标位置的棋子个数. 然后其他细节就和普通的迭代加深一样了. #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm>

bzoj 1085: [SCOI2005]骑士精神 IDA*

题目链接 给一个图, 目标位置是确定的, 问你能否在15步之内达到目标位置. 因为只有15步, 所以直接ida* #include<bits/stdc++.h> using namespace std; #define pb(x) push_back(x) #define ll long long #define mk(x, y) make_pair(x, y) #define lson l, m, rt<<1 #define mem(a) memset(a, 0, sizeof(

骑士精神——IDA*

不说了,这玩意就是个人生题,昨天晚上各种鬼,还有变量突然变成0的坑爹事情都出现了. 十五步以上算-1解... 总体思路,IDA*: 1.写个估值函数,这里我写的是和目标图的理想差异 2.枚举步数,根据步数和估值来决定剪枝. 3.一点经验,估值剪枝不要严格卡在步数上,可以适当放高上限,确保答案在搜索树上,换言之就是剪枝写的鲁棒一点,确保答案完整性,再去考虑效率. 黏上丑陋不堪的代码: 1 #include<iostream> 2 #include<cstdio> 3 #include

BZOJ 1085 SCOI 2005 骑士精神 IDA*

题目大意:有一张5*5的棋盘,上面有12和黑棋还有12个白棋.问最少多步可以到达目标状态. 思路:搜索+剪枝.至于剪枝我就用ID+A*的组合了,因为都不难想,估价函数就是当前图和目标图有多少个方块不一样.如果当前步数+估价大于当前迭代加深的层数就退出. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std;

【日常学习】【IDA*】codevs2449 骑士精神题解

题目描述 Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上. 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步数完成任务. 输入描述 Input Description 第一行有一个正整数T(T<=10),表示一共有N组数据.接下来有T个5×5的矩阵,0表示白色骑士,

BZOJ1085:[SCOI2005]骑士精神——题解+IDA*粗略讲解

http://www.lydsy.com/JudgeOnline/problem.php?id=1085 Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑 士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上. 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步数完成任务. Input 第一行有一个正整数T(T<=10)

A*算法详解 BZOJ 1085骑士精神

转载1:A*算法入门 http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx 在看下面这篇文章之前,先介绍几个理论知识,有助于理解A*算法. 启发式搜索:启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标.这样可以省略大量无畏的搜索路径,提到了效率.在启发式搜索中,对位置的估价是十分重要的.采用了不同的估价可以有不同的效果. 估价函数:从当前节点移动到目标节点的预估费用:这个估计

1288 埃及分数

1288 埃及分数 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description 在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数. 如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的. 对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越 好. 如: 19/45=1/3 + 1/12 + 1/