例题7-1
本题采用穷举,采用穷举的时候,一是注意要穷举哪个变量,第二个是确定穷举变量的取值范围。当然,取值范围越小,所用的时间越短。
#include<iostream>
#include<cstring>
using namespace std;
void int2char(int x,int xs[])
{
for(int i=4;i>0;i--)
{
xs[i]=x%10;
x=x/10;
}
xs[0]=x;
}
bool check(int xs[],int ys[])
{
int cnt[10];
memset(cnt,0,sizeof(cnt));
for(int i=0;i<5;i++)
{
if(xs[i]>=10||ys[i]>=10||++cnt[xs[i]]>1||++cnt[ys[i]]>1)
return false;
}
return true;
}
void printres(int xs[],int ys[],int n)
{
for(int i=0;i<5;i++)
cout<<ys[i];
cout<<" "<<'/'<<" ";
for(int i=0;i<5;i++)
cout<<xs[i];
cout<<" "<<'='<<" ";
cout<<n<<endl;
}
int main()
{
int n,flag=0;
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
while(cin>>n&&n)
{
if(flag++>0)
cout<<endl;//格式控制
int x,y,rightcnt=0;
int xs[5],ys[5];
for(int x=1234;x<=50000;x++)
{
y=x*n;
int2char(x,xs);
int2char(y,ys);
if(check(xs,ys))
{
printres(xs,ys,n);
rightcnt++;
}
}
if(!rightcnt)
cout<<"There are no solutions for "<<n<<"."<<endl;
}
return 0;
}
例题7-2
本题目穷举子串的起点和终点,一定要注意最后的积要使用long long 进行定义,int的表示范围不够。
#include<iostream>
using namespace std;
long long cal(int s,int e,int in[])
{
long long tmp=1;
for(int i=s;i<=e;i++)
tmp=tmp*in[i];
return tmp;
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
int inlen,in[18],rnd=1;
while(cin>>inlen&&inlen)
{
long long maxp=0,tmp;
for(int i=0;i<inlen;i++)
cin>>in[i];
for(int start=0;start<inlen;start++)
{
for(int end=start;end<inlen;end++)
{
tmp=cal(start,end,in);
if(maxp<tmp)
maxp=tmp;
}
}
cout<<"Case #"<<rnd++<<":"<<" The maximum product is "<<maxp<<"."<<endl<<endl;
}
return 0;
}
例题7-3
本题枚举y,计算x。
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=1000;
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
long long k;
while(cin>>k&&k)
{
int kans[maxn],xans[maxn],yans[maxn];
int len=0;
for(int y=k+1;y<=2*k;y++)
{
int x = k*y;
if (x % (y-k) == 0) {
x /= (y-k);
xans[len]=x;
yans[len]=y;
kans[len]=k;
len++;
}
}
cout<<len<<endl;
for (int i=0;i<len;i++)
{
cout<<"1/"<<kans[i]<<" = "<<"1/"<<xans[i]<<" + "<<"1/"<<yans[i]<<endl;
}
}
return 0;
}
例题7-4
本题采用回溯法,其实就是如果把枚举法看成解答树,就是对解答树进行减枝。
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,isp[40],vis[20],A[20];
int is_prime (int n)
{
int flag,i;
flag=1;
for(i=2;i<=sqrt(n);i++)
{
if(n%i==0)
{
flag=0;
break;
}
if(n%i==0)
{
flag=0;
break;
}
}
return flag;
}
void dfs(int cur)
{
if(cur==n&&isp[A[0]+A[n-1]])
{
for(int i=0;i<n;i++)
{
cout<<A[i];
if(i<n-1)
cout<<" ";
}
cout<<endl;
}
for(int i=2;i<=n;i++){
if(!vis[i]&&isp[A[cur-1]+i])
{
A[cur]=i;
vis[i]=1;
dfs(cur+1);
vis[i]=0;
}
}
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
memset(isp,1,sizeof(isp));
memset(vis,0,sizeof(vis));
A[0]=1;
int rnd=1;
while(cin>>n&&n)
{
if(rnd>1)
cout<<endl;
cout<<"Case "<<rnd++<<":"<<endl;
for(int i=2;i<=2*n;i++)
{
isp[i]=is_prime(i);
}
dfs(1);
}
return 0;
}
例题7-5
本题目两个return0 是精髓,判断是否有子串重复时,这个方法更加好。
#include<cstdio>
#include<cstring>
int n,L,S[100],cnt=0;
int dfs(int cur)
{
if(cnt++==n)
{
for(int i=0;i<cur;i++)
{
if(i&&i%4==0&&i%64!=0)
{
printf(" ");
}
if(i&&i%64==0)
printf("\n");
printf("%c",'A'+S[i]);
}
printf("\n");
printf("%d\n",cur);
return 0;
}
for(int i=0;i<L;i++)
{
S[cur]=i;
int ok=1;
for(int j=1;j*2<=cur+1;j++)//长度为j的子串
{
int equal = 1;
for(int k=0;k<j;k++)
{
if(S[cur-k]!=S[cur-k-j])
{
equal = 0;break;
}
}
if(equal)
{
ok=0;break;
}
}
if(ok)
{
if(!dfs(cur+1)) return 0;
}
}
return 1;
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
while(scanf("%d %d",&n,&L)==2&&n&&L)
{
cnt=0;
dfs(0);
}
return 0;
}
例题7-6
本题我在第一个代码中,并没有使用剪枝,也被接受,下一个代码使用剪枝。
#include<iostream>
#include<string>
#include<sstream>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=30;
int minbd=30,G[maxn][maxn],op[maxn];
int n=0;
int computebd(int *A,int *node,int cur,int n)//n为数组A的长度
{
int lenth=0;
for(int i=0;i<cur;i++)
{
for(int j=0;j<n;j++)
if(G[A[i]][node[j]])
{
for(int k=0;k<n;k++)
{
if((node[j]==A[k]&&lenth<abs(k-i))||lenth==0)
{
lenth=abs(k-i);
break;
}
}
}
}
return lenth;
}
void dfs(int *A,int *node,int cur)
{
if(cur==n)
{
int tmp;
tmp=computebd(A,node,n,n);
if(tmp<minbd)
{
minbd=tmp;
for (int i=0;i<n;i++)
op[i]=A[i];
}
}
// if(cur>1)
// {
// int tmp;
// tmp=computebd(A,node,cur,n);
// if(tmp>minbd)
// return;
// }
for(int i=0;i<n;i++)
{
int ok=1;
for(int j=0;j<cur;j++)
{
if(A[j]==node[i])
ok=0;
}
if(ok)
{
A[cur]=node[i];
dfs(A,node,cur+1);
}
}
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
string s;
while(getline(cin,s)&&s!="#")
{
stringstream ss(s);
int A[maxn],nodetmp[maxn],node[maxn];
n=0;
minbd=30;
memset(nodetmp,0,sizeof(nodetmp));
memset(G,0,sizeof(G));
char tmp;
int nei1,nei2;
while (ss>>tmp)
{
nei1 = tmp-'A';
nodetmp[nei1]=1;
while(ss>>tmp&&tmp!=';')
{
if(tmp!=':'&&tmp!=' ')
{
nei2 = tmp -'A';
nodetmp[nei2]=1;
G[nei1][nei2]=1;
G[nei2][nei1]=1;
}
}
}
for(int i=0;i<maxn;i++)
{
if(nodetmp[i]==1)
{
node[n++]=i;
}
}
dfs(A,node,0);
for (int i=0;i<n;i++)
{
printf("%c ",op[i]+'A');
}
cout<<"-> "<<minbd<<endl;
}
return 0;
}
通过书中给的剪枝方案进行两次剪枝后,所用时间从原来的0.12减少到0.02.效果明显。
#include<iostream>
#include<string>
#include<sstream>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=30;
int minbd=30,G[maxn][maxn],op[maxn];
int n=0;
int computebd(int *A,int *node,int cur,int n)
{
int lenth=0;
for(int i=0;i<cur;i++)
{
for(int j=0;j<n;j++)
if(G[A[i]][node[j]])
{
for(int k=0;k<cur;k++)
{
if((node[j]==A[k]&&lenth<abs(k-i))||lenth==0)
{
lenth=abs(k-i);
break;
}
}
}
}
return lenth;
}
void dfs(int *A,int *node,int cur)
{
if(cur==n)
{
int tmp;
tmp=computebd(A,node,n,n);
if(tmp<minbd)
{
minbd=tmp;
for (int i=0;i<n;i++)
op[i]=A[i];
}
}
if(cur>1)//剪枝一。
{
int tmp;
tmp=computebd(A,node,cur,n);
if(tmp>minbd)
return;
}
for(int i=0;i<n;i++)
{
int ok=1;
for(int j=0;j<cur;j++)
{
if(A[j]==node[i])
ok=0;
}
if(ok)
{
int neinum=0;//剪枝二。
for(int k=0;k<n;k++)
{
if(G[i][k])
{
neinum++;
for(int l=0;l<cur;l++)
{
if(node[k]==A[l])
neinum--;
}
}
}
if(neinum<minbd)
{
A[cur]=node[i];
dfs(A,node,cur+1);
}
}
}
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
string s;
while(getline(cin,s)&&s!="#")
{
stringstream ss(s);
int A[maxn],nodetmp[maxn],node[maxn];
n=0;
minbd=30;
memset(nodetmp,0,sizeof(nodetmp));
memset(G,0,sizeof(G));
char tmp;
int nei1,nei2;
while (ss>>tmp)
{
nei1 = tmp-'A';
nodetmp[nei1]=1;
while(ss>>tmp&&tmp!=';')
{
if(tmp!=':'&&tmp!=' ')
{
nei2 = tmp -'A';
nodetmp[nei2]=1;
G[nei1][nei2]=1;
G[nei2][nei1]=1;
}
}
}
for(int i=0;i<maxn;i++)
{
if(nodetmp[i]==1)
{
node[n++]=i;
}
}
dfs(A,node,0);
for (int i=0;i<n;i++)
{
printf("%c ",op[i]+'A');
}
cout<<"-> "<<minbd<<endl;
}
return 0;
}
例题7-7
本题目采用按照二进制方式生成子集。然后将子集分成左子树集合和右子树集合。然后分别递归建树,并且计算出组成每个树的木棍的长度。将所有通过子集建造树的最大的长度存在vector中,最后再在vector中选择出最长的。本题相当经典。采用二进制生成子集建树的方法非常值得借鉴学习。
代码来源于:书中的代码仓库。现将代码贴出,以供二次学习。
// UVa1354 Mobile Computing
// Rujia Liu
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
struct Tree {
double L, R; // distance from the root to the leftmost/rightmost point
Tree():L(0),R(0) {}
};
const int maxn = 6;
int n, vis[1<<maxn];
double r, w[maxn], sum[1<<maxn];
//sum存储了所有组成树的元素的权重和。
vector<Tree> tree[1<<maxn];
void dfs(int subset) {
if(vis[subset]) return;
vis[subset] = true;
bool have_children = false;
for(int left = (subset-1)⊂ left; left = (left-1)&subset) {
have_children = true;
int right = subset^left;
double d1 = sum[right] / sum[subset];//右子树的重量在整个树中所占比例。
double d2 = sum[left] / sum[subset];//左子树的重量在整个树中所占比例。也就是右子树的木棍的长度
dfs(left); dfs(right);
for(int i = 0; i < tree[left].size(); i++)
for(int j = 0; j < tree[right].size(); j++) {
Tree t;
t.L = max(tree[left][i].L + d1, tree[right][j].L - d2);
t.R = max(tree[right][j].R + d2, tree[left][i].R - d1);
if(t.L + t.R < r) tree[subset].push_back(t);
}
}
if(!have_children) tree[subset].push_back(Tree());
}
int main() {
//freopen("datain.txt","r",stdin);
int T;
scanf("%d", &T);
while(T--) {
scanf("%lf%d", &r, &n);
for(int i = 0; i < n; i++) scanf("%lf", &w[i]);
for(int i = 0; i < (1<<n); i++) {
sum[i] = 0;
tree[i].clear();
for(int j = 0; j < n; j++)
if(i & (1<<j)) sum[i] += w[j];
//生成权重集合的子集,并进行加存入sum中。
}
int root = (1<<n)-1;
memset(vis, 0, sizeof(vis));
dfs(root);
double ans = -1;
for(int i = 0; i < tree[root].size(); i++)
ans = max(ans, tree[root][i].L + tree[root][i].R);
printf("%.10lf\n", ans);
}
return 0;
}
例题7-8
本题采用书中代码,我将书中代码进行注释,以便理解。其实本题就是加上一个状态变量的BFS,采用BFS的基本结构,使用队列进行辅助。
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct Node
{
int v[3],dist;//存储3个水杯中水的量,dist存储的为倒水的量。
bool operator < (const Node& rhs) const {
return dist > rhs.dist;//要使用优先队列,定义小于符号
}
};
const int maxn=200+5;
int vis[maxn][maxn],cap[3],ans[maxn];
//vis存储这个状态是否已经到达,因为已知。
void update_ans (const Node& u)
{
for(int i=0;i<3;i++)
{
int d = u.v[i];
if(ans[d]<0||u.dist<ans[d]) ans[d]=u.dist;
//有一个水杯中有d升水的最少要倒的水存储在ans中。
}
}
void solve(int a,int b, int c, int d)
{
cap[0]=a; cap[1]=b; cap[2]=c;
memset(vis,0,sizeof(vis));
memset(ans,-1,sizeof(ans));
priority_queue<Node> q;
Node start;
start.dist=0;
start.v[0]=0;start.v[1]=0;start.v[2]=c;
//初始时,只有第三个杯子有C升水。
q.push(start);
vis[0][0]=1;
while(!q.empty())
{
Node u = q.top(); q.pop();
update_ans(u);
if(ans[d]>=0) break;//如果已经达到目标,或者已经超过d了。
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(i!=j)//选中两个杯子,从杯子i往杯子j倒。
{
if(u.v[i]==0 || u.v[j]==cap[j]) continue;
//如果i杯子中没水或者j杯子中水已满,那么不能倒水。
int amount = min(cap[j],u.v[i]+u.v[j])-u.v[j];
//倒水,但不溢出。
Node u2;
memcpy(&u2,&u,sizeof(u));
u2.dist=u.dist+amount;
u2.v[i]-=amount;
u2.v[j]+=amount;
if(!vis[u2.v[0]][u2.v[1]])
{
vis[u2.v[0]][u2.v[1]]=1;
q.push(u2);
}
}
}
while(d>=0)
{
if(ans[d]>=0)
{
printf("%d %d\n",ans[d],d);
return;
}
d--;//没有符合条件的d,则减少1,重新搜索。
}
}
int main()
{
int T,a,b,c,d;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
solve(a,b,c,d);
}
return 0;
}
习题7-9
本题采用先重新生成一张图,对不是障碍的格子进行编号,并存储它的x和y。然后再计算并存储不是障碍的格子能够移动一步能够到达的格子的坐标,也就是它的邻居,存储起来,这样减少bfs中的搜索步数。
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 256;
int dx[] = {-1,1,0,0,0};
int dy[] = {0,0,0,1,-1};
int s[3],e[3];
int neicnt[maxn],nei[maxn][maxn];
int vis[maxn][maxn][maxn];
struct Node
{
int cntx,cnty,cntz;
int d;
Node():cntx(-1),cnty(-1),cntz(-1),d(-1) {}
};
bool judge(int cntx,int cnty,int ncntx,int ncnty)
{
return ncntx==ncnty||(cntx==ncnty&&cnty==ncntx);
}
int bfs()
{
queue<Node> q;
Node start;
start.cntx=s[0];
start.cnty=s[1];
start.cntz=s[2];
start.d=0;
memset(vis,-1,sizeof(vis));
q.push(start);
while(!q.empty())
{
Node u = q.front();
q.pop();
if(u.cntx==e[0]&&u.cnty==e[1]&&u.cntz==e[2]) return u.d;
for (int i=0;i<neicnt[u.cntx];i++)
{
int ncntx = nei[u.cntx][i];
for(int j=0;j<neicnt[u.cnty];j++)
{
int ncnty = nei[u.cnty][j];
if(judge(u.cntx,u.cnty,ncntx,ncnty)) continue;
for (int k=0;k<neicnt[u.cntz];k++)
{
int ncntz = nei[u.cntz][k];
if(judge(u.cntx,u.cntz,ncntx,ncntz)) continue;
if(judge(u.cntz,u.cnty,ncntz,ncnty)) continue;
if(vis[ncntx][ncnty][ncntz]!=-1) continue;
vis[ncntx][ncnty][ncntz]=0;
Node v;
v.cntx=ncntx;
v.cnty=ncnty;
v.cntz=ncntz;
v.d=u.d+1;
q.push(v);
}
}
}
}
return -1;
}
int main()
{
//freopen("datain.txt","r",stdin);
int h,w,n;
while(cin>>w>>h>>n&&h)
{
char map[maxn][maxn];
int new_map[maxn][maxn];
int idx[maxn],idy[maxn];
int cnt=0;
getchar();
for(int i=0;i<h;i++)
{
fgets(map[i],20,stdin);
}
for(int i=0;i<h;i++)
{
for(int j=0;j<w;j++)
{
if(map[i][j]!='#')
{
idx[cnt]=i;
idy[cnt]=j;
new_map[i][j]=cnt;
if(islower(map[i][j]))
{
s[map[i][j]-'a']=cnt;
}
if(isupper(map[i][j]))
{
e[map[i][j]-'A']=cnt;
}
cnt++;
}
}
}
//寻找每个空格合法的相邻点
for(int i=0;i<cnt;i++)
{
neicnt[i]=0;
for(int j=0;j<5;j++)
{
int nx=idx[i]+dx[j];
int ny=idy[i]+dy[j];
if(map[nx][ny]!='#'&&nx>=0&&ny>=0)
{
nei[i][neicnt[i]++]=new_map[nx][ny];
}
}
}
//增加虚拟节点,值得借鉴。
if(n<=2)
{
neicnt[cnt]=1;nei[cnt][0]=cnt;s[2]=e[2]=cnt++;
}
if(n<=1)
{
neicnt[cnt]=1;nei[cnt][0]=cnt;s[1]=e[1]=cnt++;
}
cout<<bfs()<<endl;
}
return 0;
}
例题7-10
IDA*例题,使用点击打开链接的代码,我给这个链接的代码进行注释,对IDA*进行学习。其中有很多技巧可以学习。我参考了这篇文章的代码,对于自己不懂的地方进行了重写,IDA*的框架将在总结中予以呈现。
#include <bits/stdc++.h>
//基本包含所有库文件
using namespace std;
struct State{
int a[12];
}; //保存状态
int n, kase = 0, maxd;
//n为排列元素的个数,kase为轮数,maxd为IDA*中最大深度。
int getH(State cur)
{
int cnt = 0;
for(int i = 0; i < n-1; ++i)
if(cur.a[i]+1 != cur.a[i+1])
cnt++;
return cnt;
}//或者乐观估计函数,也就是顺序不同的元素的个数
bool DFS(int d, State u)
{
int H = getH(u);
if(d == maxd) return H == 0;
if(3*d + H > 3*maxd) return false;
int board[12];
for(int len = 1;len<n;len++)//剪切的段的数量
{
for(int pos=0;pos<=n-len;pos++)//剪切的段的位置
{
State w,v;
memcpy(w.a, u.a, sizeof(u.a));
for (int i=0,j=pos;i<len;i++,j++)//粘贴板内容
{
board[i]=u.a[j];
}
for (int i=pos,j=len+pos;j<n;i++,j++)//剪切后剩下的内容
{
w.a[i] = u.a[j];
}
int newlen = n - len + 1;
for(int posi=0;posi<newlen&&posi!=pos;posi++)//粘贴位置
{
for(int k=0;k<posi;k++)
v.a[k]=w.a[k];
for(int k=0;k<len;k++)
v.a[posi+k]=board[k];
for(int i=posi+len,j=posi;i<n;i++,j++)
v.a[i]=w.a[j];
if(DFS(d + 1, v)) return true;
}
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
while(cin >> n && n){
State beg;
for(int i = 0; i < n; ++i)
cin >> beg.a[i];
for(maxd = 0; ; ++maxd)
if(DFS(0, beg))
break;
printf("Case %d: %d\n", ++kase, maxd);
}
return 0;
}
例7-11
简单枚举,在枚举前,预计计算规模,根据不同的数据采用不同的枚举策略,减小搜索空间。
#include <bits/stdc++.h>
using namespace std;
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
ios::sync_with_stdio(false);
int T,rnd=1;
cin>>T;
while(T--)
{
long long N,S1,V1,S2,V2;
long long bestvalue=0;
cin>>N>>S1>>V1>>S2>>V2;
if(N/S1<65536)
{
for(int i=0;i*S1<=N;i++)
{
long long value = i*V1+((N-i*S1)/S2)*V2;
bestvalue=max(bestvalue,value);
}
}
else if(N/S2<65536)
{
for(long long i=0;i*S2<=N;i++)
{
if(i*S2<=N)
{
long long value = i*V2+((N-i*S2)/S1)*V1;
bestvalue=max(bestvalue,value);
}
}
}
else
{
if (S2*V1 < S1*V2)
{
for(int i=0;i<S2;i++)//枚举1的数量。
{
if(i*S1<=N)
{
long long value = i*V1+((N-i*S1)/S2)*V2;
bestvalue=max(bestvalue,value);
}
}
}
else
{
for(int i=0;i<S1;i++)//枚举2的数量。
{
if(i*S2<=N)
{
long long value = i*V2+((N-i*S2)/S1)*V1;
bestvalue=max(bestvalue,value);
}
}
}
}
cout<<"Case #"<<rnd++<<": "<<bestvalue<<endl;
}
return 0;
}
例题7-12
本次我开始使用状态更新的方法,但是苦于很能去存储已经访问过的状态,相较于8数码问题,这道题一共有27个值的变化,不可能像八数码问题那样,直接将27个值变为一个27个值,导致进行了很多无谓的搜索,这样整个搜索空间太大,要存储的东西太多。但是,本题提供了第二种使用IDA*的方法。
第一种:状态空间搜索(半成品)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 60000;
struct State
{
int board[8][8];
char operate;
int fid;
int id;
int d;
};
State state[maxn];
int cnt=0;
void Rotation(State &s,int rc,int flag)
//1表示向上滚动,2表示向下,3表示向右,4表示向左。
{
if(flag==1)
{
int tmp = s.board[0][rc];
for(int i=0;i<6;i++)
{
s.board[i][rc]=s.board[i+1][rc];
}
s.board[6][rc]=tmp;
}
else if(flag==2)
{
int tmp = s.board[6][rc];
for(int i=6;i>0;i--)
{
s.board[i][rc]=s.board[i-1][rc];
}
s.board[0][rc]=tmp;
}
else if(flag==3)
{
int tmp = s.board[rc][6];
for(int i=6;i>0;i--)
{
s.board[rc][i]=s.board[rc][i-1];
}
s.board[rc][0]=tmp;
}
else if (flag==4)
{
int tmp = s.board[rc][0];
for(int i=0;i<6;i++)
{
s.board[rc][i]=s.board[rc][i+1];
}
s.board[rc][6]=tmp;
}
}
void operation(State &s,char x)
{
if(x=='A')
{
Rotation(s,2,1);
}
else if (x=='B')
{
Rotation(s,4,1);
}
else if (x=='C')
{
Rotation(s,2,3);
}
else if (x=='D')
{
Rotation(s,4,3);
}
else if (x=='E')
{
Rotation(s,4,2);
}
else if (x=='F')
{
Rotation(s,2,2);
}
else if (x=='G')
{
Rotation(s,4,4);
}
else if (x=='H')
{
Rotation(s,2,4);
}
}
bool isdone(State &s,int num)
{
if(s.board[3][2]!=num||s.board[3][4]!=num)
{
return false;
}
else
{
for(int i=2;i<=4;i++)
{
if(s.board[2][i]!=num)
{
return false;
}
if(s.board[4][i]!=num)
{
return false;
}
}
}
return true;
}
State solve(State &s,int num)
{
if(isdone(s,num)) return s;
queue<State> qs;
qs.push(s);
while(!qs.empty())
{
State s1 = qs.front();
if(s1.d>5) return s1;
qs.pop();
for(int i=0;i<=7;i++)
{
State s2;
memcpy(&s2,&s1,sizeof(s1));
char op = 'A'+ i;
operation(s2,op);
s2.operate = op;
s2.d=s1.d+1;
s2.fid=s1.id;
s2.id=cnt;
state[cnt++]=s2;
if(isdone(s2,num)) return s2;
qs.push(s2);
}
}
}
int main()
{
freopen("datain.txt","r",stdin);
int map[8][8];
memset(map,0,sizeof(map));
while(cin>>map[0][2]&&map[0][2])
{
cin>>map[0][4];
cin>>map[1][2]>>map[1][4];
for(int i=0;i<7;i++)
{
cin>>map[2][i];
}
cin>>map[3][2]>>map[3][4];
for(int i=0;i<7;i++)
{
cin>>map[4][i];
}
cin>>map[5][2]>>map[5][4];
cin>>map[6][2]>>map[6][4];
cnt=0;
State ans[4];
for(int num=1;num<=3;num++)
{
State root;
memset(root.board,0,sizeof(root.board));
for(int i=0;i<7;i++)
for (int j=0;j<7;j++)
if(map[i][j]==num)
root.board[i][j]=num;
root.d = 0;
root.id = cnt;
root.fid = 0;
state[cnt++]=root;
ans[num]=solve(root,num);
}
int mind=10;
int index;
for(int i=1;i<=3;i++)
{
if(mind>ans[i].d)
{
mind=ans[i].d;
index = i;
}
}
char charo[maxn];
int j=0;
if (ans[index].d==0)
{
cout<<"No moves needed";
}
else
{
for(int i=ans[index].id;state[i].fid!=0;i=state[i].fid)
{
charo[j]=state[i].operate;
j=j+1;
}
for (int i=j-1;i>=0;i--)
{
cout<<charo[i];
}
}
cout<<endl<<index<<endl;
}
return 0;
}
第二种,采用IDA*。其中H函数的设计进行了参考,最开始自己设计的H函数要导致超时,所以出现了问题。本题目实际上可以采用编号存储为一维数组,在进行A-H的操作中,实际上可以直接列出索引,减少代码量,而我却采用了修改二维数组的方式,显然比较麻烦。这些地方都是值得借鉴的。采用IDA*,对于输出路径很有帮助,因为它是DFS的操作,而使用状态空间法,要存储父节点,导致很多时候,并没有足够大的空间进行存储。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 60000;
struct State
{
int board[8][8];
};
int maxd,fsnum;
char fs[maxn];
void Rotation(State &s,int rc,int flag)
//1表示向上滚动,2表示向下,3表示向右,4表示向左。
{
if(flag==1)
{
int tmp = s.board[0][rc];
for(int i=0;i<6;i++)
{
s.board[i][rc]=s.board[i+1][rc];
}
s.board[6][rc]=tmp;
}
else if(flag==2)
{
int tmp = s.board[6][rc];
for(int i=6;i>0;i--)
{
s.board[i][rc]=s.board[i-1][rc];
}
s.board[0][rc]=tmp;
}
else if(flag==3)
{
int tmp = s.board[rc][6];
for(int i=6;i>0;i--)
{
s.board[rc][i]=s.board[rc][i-1];
}
s.board[rc][0]=tmp;
}
else if (flag==4)
{
int tmp = s.board[rc][0];
for(int i=0;i<6;i++)
{
s.board[rc][i]=s.board[rc][i+1];
}
s.board[rc][6]=tmp;
}
}
void operation(State &s,char x)
{
if(x=='A')
{
Rotation(s,2,1);
}
else if (x=='B')
{
Rotation(s,4,1);
}
else if (x=='C')
{
Rotation(s,2,3);
}
else if (x=='D')
{
Rotation(s,4,3);
}
else if (x=='E')
{
Rotation(s,4,2);
}
else if (x=='F')
{
Rotation(s,2,2);
}
else if (x=='G')
{
Rotation(s,4,4);
}
else if (x=='H')
{
Rotation(s,2,4);
}
}
int isdone(State &s)
{
for (int num=1;num<=3;num++)
{
bool flag = true;
if(s.board[3][2]!=num||s.board[3][4]!=num)
{
flag = false;
}
else
{
for(int i=2;i<=4;i++)
{
if(s.board[2][i]!=num)
{
flag = false;
}
if(s.board[4][i]!=num)
{
flag = false;
}
}
}
if(flag)
{
return num;
}
}
return 0;
}
int getH(State &s,int num)
{
int cnt = 0;
for(int i=2;i<=4;i++)
{
if(s.board[2][i]!=num)
cnt++;
if(s.board[3][i]!=num&&i!=3)
cnt++;
if(s.board[4][i]!=num)
cnt++;
}
return cnt;
}
int get_h(State &s)
{
return min(getH(s,1),min(getH(s,2),getH(s,3)));
}
bool dfs(int d,State s)
{
int h=get_h(s);
if(d==maxd)
{
fsnum=isdone(s);
if(fsnum)
{
if(d==0)
{
cout<<"No moves needed"<<endl;
return true;
}
else
{
fs[d]='\0';
cout<<fs<<endl;
return true;
}
}
else
return false;
}
if(d+h>maxd) return false;
for(int i=0;i<=7;i++)
{
State s2;
memcpy(&s2,&s,sizeof(s));
char op = 'A'+ i;
operation(s2,op);
fs[d]=op;
if(dfs(d+1,s2)) return true;
}
return false;
}
int main()
{
//freopen("datain.txt","r",stdin);
int map[8][8];
memset(map,0,sizeof(map));
while(cin>>map[0][2]&&map[0][2])
{
cin>>map[0][4];
cin>>map[1][2]>>map[1][4];
for(int i=0;i<7;i++)
{
cin>>map[2][i];
}
cin>>map[3][2]>>map[3][4];
for(int i=0;i<7;i++)
{
cin>>map[4][i];
}
cin>>map[5][2]>>map[5][4];
cin>>map[6][2]>>map[6][4];
bool flag=false;
for(maxd=0; ;maxd++)
{
State root;
memcpy(root.board,map,sizeof(map));
flag=dfs(0,root);
if(flag)
{
cout<<fsnum<<endl;
break;
}
if(flag)
break;
}
}
return 0;
}
例题7-12
本题目解法比较经典,还涉及到旋转、翻转等操作。本文参考博客点击打开链接的解法和代码,对代码进行注释学习。在数据上,采用双层的set进行存储,以判断之前没有访问过这个节点。非常值得借鉴。本题将一个大规模的问题分解成为一个小规模的问题,应该在第八章会有所学习。
#include <bits/stdc++.h>
using namespace std;
struct Cell
{
int x,y;
Cell(int x=0,int y=0):x(x),y(y){};
bool operator < (const Cell & rhs) const
{
return x<rhs.x ||(x==rhs.x&&y<rhs.y);
}
//因为要使用set,所以定义<符号
};
typedef set<Cell> Polyomino;
//将set<Cell>类型定义为Polyomino类型
#define FOR_CELL(c,p) for(Polyomino::const_iterator c = (p).begin();c != (p).end(); c++)
inline Polyomino normalize(const Polyomino &p)//标准化,将连通块平移到原点
{
int minX = p.begin()->x,minY=p.begin()->y;
FOR_CELL(c,p)
{
minX=min(minX,c->x);
minY=min(minY,c->y);
}
Polyomino p2;
FOR_CELL(c,p)
{
p2.insert(Cell(c->x-minX,c->y-minY));
}
return p2;
}
inline Polyomino rotate(const Polyomino &p)
//以原点为中心,顺时针旋转90度
{
Polyomino p2;
FOR_CELL(c,p)
{
p2.insert(Cell(c->y,-c->x));
}
return normalize(p2);
}
inline Polyomino flip(const Polyomino &p)
{
//沿X轴翻转
Polyomino p2;
FOR_CELL(c,p)
{
p2.insert(Cell(c->x,-c->y));
}
return normalize(p2);
}
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int maxn=10;
set<Polyomino> poly[maxn+1];
int ans[maxn+1][maxn+1][maxn+1];
void check_polyomino(const Polyomino& p0,const Cell& c)
{
//检查C添加到p0种是否产生重复
Polyomino p = p0;
p.insert(c);
p= normalize(p);
int n=p.size();//n 表示为n连通块。
for (int i=0;i<4;i++)
{
if(poly[n].count(p)!=0) return;
p = rotate(p);
}
p=flip(p);
for (int i=0;i<4;i++)
{
if(poly[n].count(p)!=0) return;
p = rotate(p);
}
poly[n].insert(p);
//没有重复的,插入保存,有重复的话就已经结束返回。
}
void generate()
{
Polyomino s;
s.insert(Cell(0,0));
poly[1].insert(s);
//初始化。
for (int n=2;n<=maxn;n++)
{
for(set<Polyomino>::iterator p=poly[n-1].begin();p!=poly[n-1].end();++p)
FOR_CELL(c,*p)
for (int dir=0;dir<4;dir++)
{
Cell newc(c->x+dx[dir],c->y+dy[dir]);
if(p->count(newc)==0)
check_polyomino(*p,newc);
}
}
for(int n=1;n<=maxn;n++)
for(int w=1;w<=maxn;w++)
for(int h=1;h<=maxn;h++)
{//数符合要求的连通块的个数
int cnt=0;
for(set<Polyomino>::iterator p=poly[n].begin();p!=poly[n].end();++p)
{
int maxX=0,maxY=0;
FOR_CELL(c,*p)
{
maxX=max(maxX,c->x);
maxY=max(maxY,c->y);
}
if(min(maxX,maxY)<min(h,w)&&max(maxX,maxY)<max(h,w))
++cnt;
}
ans[n][w][h]=cnt;
}
}
int main()
{
generate();
int n,w,h;
while(cin>>n>>w>>h&&n)
{
cout<<ans[n][w][h]<<endl;
}
return 0;
}
习题7-1
本题使用BFS来判断连通性,使用DFS来寻找路径。
#include<bits/stdc++.h>
using namespace std;
const int maxn=100;
int mapp[maxn][maxn];
int node=1,endnode;
int vis[maxn];
int sum=0;
int added[maxn];
void dfs(int d,int* anstmp)
{
if(anstmp[d]==endnode)
{
for(int i=0;i<d;i++)
cout<<anstmp[i]<<" ";
cout<<endnode<<endl;
sum++;
return ;
}
else
{
for (int i=1;i<=node;i++)
{
if(mapp[anstmp[d]][i]==1&&vis[i]!=1)
{
vis[i]=1;
anstmp[d+1]=i;
dfs(d+1,anstmp);
vis[i]=0;
}
}
}
return ;
}
bool isconnected(int endnode)
{
if(endnode==1)
return true;
queue<int> q;
q.push(1);
while(!q.empty())
{
int ne=q.front();
q.pop();
for (int i=1;i<=node;i++)
{
if(mapp[ne][i]==1&&added[i]!=1)
{
if(i==endnode)
return true;
else
{
added[i]=1;
q.push(i);
}
}
}
}
return false;
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
int rnd=1;
while(cin>>endnode)
{
int r,c;
memset(mapp,0,sizeof(mapp));
sum=0;
node=1;
while(cin>>r>>c&&r)
{
node=max(c,max(node,r));
mapp[r][c]=1;
mapp[c][r]=1;
}
int anstmp[maxn];
memset(vis,0,sizeof(vis));
anstmp[0]=1;
vis[1]=1;
cout<<"CASE "<<rnd++<<":"<<endl;
memset(added,0,sizeof(added));
if(isconnected(endnode))
{
dfs(0,anstmp);
}
cout<<"There are "<<sum<<" routes from the firestation to streetcorner "<<endnode<<"."<<endl;
}
return 0;
}
习题7-2(WA)
本题就是一个dfs+剪枝,因为没有清楚的理解题意,导致一直WA。最终因为没有足够的测试数据,于是就暂且放弃,进行下一道题。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
int center=maxn/2;
int mapp[maxn][maxn],vis[maxn][maxn];
int sumr=0,maxd;
struct State
{
int x,y;
int dir;
};
char news[]={'e','n','s','w'};
int dx[]={1,0,0,-1};
int dy[]={0,1,-1,0};
void dfs(int d,State *route)
{
if(route[d].x==center&&route[d].y==center&&d==maxd)
{
for(int i=1;i<=d;i++)
cout<<news[route[i].dir];
cout<<endl;
sumr++;
return;
}
if(d==maxd) return;
for(int i=0;i<4;i++)
{
if(route[d].dir==1||route[d].dir==2)
{
if(i==1||i==2) continue;
}
if(route[d].dir==0||route[d].dir==3)
{
if(i==0||i==3) continue;
}
int nx,ny,ok1=1,ok2=1;
for(int j=1;j<=d+1;j++)
{
nx = route[d].x+dx[i]*(j);
ny = route[d].y+dy[i]*(j);
if(mapp[nx][ny]==1)
{
ok1=0;
break;
}
if(vis[nx][ny]==1)
{
ok2=0;
break;
}
}
if(ok2&&ok1)
{
for(int j=1;j<=d+1;j++)
{
nx = route[d].x+dx[i]*(j);
ny = route[d].y+dy[i]*(j);
vis[nx][ny]=1;
}
State nstate;
nstate.x= nx;
nstate.y= ny;
nstate.dir=i;
memcpy(&route[d+1],&nstate,sizeof(nstate));
dfs(d+1,route);
for(int j=1;j<=d+1;j++)
{
nx = route[d].x+dx[i]*(j);
ny = route[d].y+dy[i]*(j);
vis[nx][ny]=0;
}
}
}
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
int T;
cin>>T;
while(T--)
{
sumr=0;
memset(mapp,0,sizeof(mapp));
memset(vis,0,sizeof(vis));
int numb;
cin>>maxd>>numb;
for(int i=0;i<numb;i++)
{
int x,y,nx,ny;
cin>>x>>y;
nx=center+x;
ny=center+y;
mapp[nx][ny]=1;
}
State route[maxn];
State br;
br.x=center;
br.y=center;
br.dir=4;
memcpy(&route[0],&br,sizeof(br));
dfs(0,route);
if(sumr==0)
cout<<endl;
cout<<"Found "<<sumr<<" golygon(s)."<<endl<<endl;
}
return 0;
}