算法竞赛入门经典(第2版)-刘汝佳-第七章解题源码(C++语言)(部分)

例题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;
} 





算法竞赛入门经典——训练指南》代码仓库 例题代码 限于篇幅,书上并没有给出所有例题的代码,这里给出了所有例题的代码,并且改进了书上的一些代码。 第一章 32题 38份代码 第二章 28题 30份代码 第三章 22题 23份代码 第四章 19题 21份代码 第五章 34题 39份代码 第六章 24题 26份代码 共159题 177份代码 为了最大限度保证代码风格的一致性,所有例题代码均由刘汝佳C++语言编写。 所有代码均通过了UVa/La的测试,但不能保证程序是正确的(比如数据可能不够强),有疑问请致信rujia.liu@gmail.com,或在googlecode中提出: http://code.google.com/p/aoapc-book/ [最新更新] 2013-04-23 增加字符串中例题10(UVa11992 Fast Matrix Operations)的另一个本的程序,执行效率较低,但更具一般性,可读性也更好 2013-04-22 增加字符串部分“简易搜索引擎”代码,可提交到UVa10679 2013-04-13 修正Treap中优先级比较的bug(原来的代码实际上是在比较指针的大小!),加入纯名次树代码 2013-03-31 修正UVa1549标程的bug,即buf数组不够大。 增加线段树部分“动态范围最小值”的完整代码 2013-03-23 修正UVa10054标程的bug,即没有判断是否每个点的度数均为偶数。UVa数据已经更新 LA3401修正了代码和文字不一致的问题 UVa11270增加了答案缓存 2013-03-21 增加线段树部分中两个经典问题的完整代码:快速序列操作I和快速序列操作II 2013-02-28 补全所有159道例题的代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值