迭代,IDA*
1.codevs1288
题意:对于一个分数a/b(a!=1),将它表示为1/x + 1/y + 1/z ……的形式,x,y,z……互不相同
多解取加数少的,加数相同时,取最小的分数最大的。
思路:经典基础IDA*
搜索无指定界限所以手动规定。因为要求分母尽量小,所以先找最小分母做下界
然后规定层数迭代搜 ans存分母
因为从小到大依次搜,层数加深,第一次找到的一定最优。
估价函数:若扩展到i层时,前i个分数之和为c/d,第i个分数为1/e
因为分母递增,所以接下来至少还需要>(a/b-c/d)/(1/e)个分数,总和才能到a/b.
此估价函数可以估计出最少多少步可以到达解,也就是说限定了层数。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 10001 #define ll long long using namespace std; int minn; ll a,b,deep; ll ans[N],v[N]; inline ll read() { ll x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } inline ll gcd(ll x,ll y) { if(x<y) x^=y,y^=x,x^=y; int tmp; while(y){ tmp=x%y;x=y;y=tmp; }return x; } inline bool better(int d) { for(int i=d;i>=0;i--) return ans[i]==-1 || v[i]<ans[i]; return false; } inline int get(ll x,ll y)//当前下界 { for(int i=2;;++i) if(y<x*i) return i; } bool IDA(int d,int minn,ll aa,ll bb) { if(d==deep) { if(bb%aa) return false;//分子必须是1.因为已经约分,不必但心aa不为1 v[d]=bb/aa; if(better(d)) memcpy(ans,v,sizeof(ll)*(d+1)); return true; } bool flag=false; minn=std::max(minn,get(aa,bb));//也算剪枝,minn在不断增大 for(int i=minn;;++i) { if(bb*(deep-d+1)<=i*aa) break; //估价函数:因为i在增大,所以如果剩下的deep-d+1个分数全部都是1/i,加起来仍然不超过aa/bb,则无解,需要阔搜索层数 v[d]=i; ll b2=bb*i,a2=aa*i-bb;//计算aa/bb-1/i ll g=gcd(a2,b2); if(IDA(d+1,minn+1,a2/g,b2/g)) flag=true; } return flag; } int main() { a=read();b=read(); minn=get(a,b); for(deep=1;;deep++) { memset(ans,-1,sizeof ans);//don't forget if(IDA(0,get(a,b),a,b))//get 得到搜索下界 break; } for(int i=0;i<=deep;++i) printf("%d ",ans[i]); return 0; } Code
2.codevs 2541
题意:给定n计算m^n的最少运算次数。在运算的每一步,都应该是m的正整数次方
思路:迭代加深
同样的维护已经得到的mi数组
数组的大小对应做了几次运算
加上几个剪枝:
如果mi中最大的<<(deep-k) 都到不了n 搜索失败
生成新的mi的时候 尽量组合数大的 这样也可以减小规模
#include<iostream> #include<cstdio> #include<cstring> #define N 101 using namespace std; int n,a[N<<2]; int dfs(int k,int deep) { if(a[k]==n) return deep; if(deep==k) return 0; int maxx=0; for(int i=0;i<=k;i++)maxx=max(maxx,a[k]); if(maxx<<(deep-k)<n)return 0; for(int i=k;i>=0;i--) { a[k+1]=a[k]+a[i]; if(dfs(k+1,deep)) return 1; a[k+1]=a[k]-a[i]; if(dfs(k+1,deep)) return 1; }return 0; } int main() { scanf("%d",&n); if(n==1) { printf("0\n"); return 0; } a[0]=1; for(int i=1;i<=N;i++) if(dfs(0,i)) { printf("%d\n",i); return 0; } return 0; }
3.codevs2495题意:
地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。 水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。想知道最少要多少步才能把所有格子的颜色变成一样的。
思路:
左上角的格子所在的联通块里的格子标记为1。左上角联通块周围一圈格子标记为2,
其它格子标记为0。如果某次选择了颜色c,
只需要找出标记为2并且颜色为c的格子,向四周扩展
,并相应地修改v标记,就可以不断扩大标记为1的区域,
最终如果所有格子标记都是1
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int s,n,map[9][9],mark[9][9]; int xx[4]= {1,-1,0,0},yy[4]= {0,0,-1,1},used[6]; bool ans; int get() { int t=0; memset(used,0,sizeof(used)); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(!used[map[i][j]]&&mark[i][j]!=1) { used[map[i][j]]=1; t++; }return t; } void dfs(int a,int b,int x) { mark[a][b]=1; for(int i=0; i<4; i++) { int nowx=a+xx[i],nowy=b+yy[i]; if(nowx<1||nowy<1||nowx>n||nowy>n||mark[nowx][nowy]==1)continue; mark[nowx][nowy]=2; if(map[nowx][nowy]==x)dfs(nowx,nowy,x); } } int fill(int x) { int t=0; for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(mark[i][j]==2&&map[i][j]==x) {t++;dfs(i,j,x);} return t; } void search(int k) { int v=get(); if(!v)ans=1; if(k+v>s||ans)return; int temp[10][10]; for(int i=0; i<=5; i++) { memcpy(temp,mark,sizeof(mark)); if(fill(i))search(k+1); memcpy(mark,temp,sizeof(mark)); } } int main() { while(1) { memset(mark,0,sizeof(mark)); scanf("%d",&n); ans=0; if(n==0)break; for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) scanf("%d",&map[i][j]); dfs(1,1,map[1][1]); for(s=0;;s++) { search(0); if(ans){printf("%d\n",s); break;} } } return 0; }
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。