紫薯总览——AC代码+小题解

目录

目录

  • 第六讲——数据结构基础
    • 6.1栈和队列
      • 例题6-2 UVa514
      • 例题6-3 UVa442
    • 6.2链表
      • 例题6-4 UVa11988
      • 例题6-5 UVa12657
    • 6.3树和二叉树
      • 例题6-6 UVa679
      • 例题6-9 UVa839
      • 例题6-10 UVa699
    • 6.4图
      • 例题6-15 UVa10305
      • 例题6-16 UVa10129
  • 第七讲——暴力枚举
    • 7.1简单枚举
      • 例题7-1 UVa725
      • 例题7-2 UVa11059
      • 例题7-3 UVa10976
    • 7.1回溯法
      • 例题7-4 UVa524
      • 例题7-5 UVa129
      • 例题7-6 UVa140
      • 例题7-7 UVa1354
    • 7.5 路径寻找问题
      • 例题7-8 UVa1354
      • 例题7-9 UVa1601
    • 7.6 迭代加深搜索
      • 例题7-9 UVa1601
      • 例题7-10 UVa11212
  • 第八讲——高效算法设计
  • 第九讲——动态规划初步
    • 9.1 数字三角形
      • 9.1.1数字三角形
      • 数字三角形
    • 9.2 DAG上的动态规划
      • 9.2.1DAG模型
      • 矩形嵌套问题
      • 硬币问题
      • 9.2.4 小结与应用举例
      • 例题9.2 巴比伦塔
    • 9.3 多阶段决策问题
      • 9.3.1 多段图的最短路
      • 例题9.4 单项TSP
      • 9.3.2 0-1背包问题
      • 物品无限的背包问题(完全背包)
      • 0-1背包问题
      • 例题9.5 劲歌金曲
    • 9.4 更多经典模型
      • 9.4.1 线性结构上的动态规划
        • 9.4.1 最长上升自序列(LIS)
  • 连环大跳(跳了很多)
  • 第十讲——数据结构基础
    • 10.1 数论初步
      • 10.1.1 欧几里得算法和唯一分解定理

第六讲——数据结构基础

6.1栈和队列

例题6-2 UVa514

我就是入门半年,还没入门成功的大——

从现在起开始认真刷紫薯(其实是抄紫薯哈哈哈)
每道题的理解都在代码里面。
我是真的弱,如果我有错误,评论里留下你的大名,我一定记在我的报答簿上。

正文

紫薯总览——AC代码+小题解_第1张图片
让我们一起————入门到入土
话不多说,立马进入正题
不愧是大佬,思路如此清晰
后知后觉——好像应该挂出链接——戳我戳我

#include
#include
#include
#include
#include
#include  
using namespace std;
#define mid   100007
typedef long long ll;
//紫薯上的题
//就是看出栈顺序能够不能实现
//1。检测此时该入栈的数是否对
//2.检测栈顶的数是否对
//3.两者都不满足就入栈(这样就可以调换顺序)
//tips:为了学好思路,这些都是请参考过紫薯的,不过代码是自己打的 
int main(){
	int a,c[1006],i,t;
	while(cin>>a&&a!=0){
		while(1){
			t=0;
		cin>>c[1];
		if(c[1]==0)
		break;  
		for(i=2;i<=a;i++)cin>>c[i];//这是要求最后的顺序 
		stack<int>w;
		int e=1,f=1;
		while(e<=a)/*全部输出都能够一一对应*/{
			if(f==c[e])e++,f++;//满足1
			else if(!w.empty()/*栈里有东西*/&&w.top()==c[e]){
			w.pop();
			e++;
		}
			else if(f<=a)/*保证还可以入栈,就可以换顺序*/{
			w.push(f);
			f++;
			} 
			else /*上面都不满足就不可能了*/{t=1;break;} 
		}
		printf("%s\n",t==0?"Yes":"No");
	}
	cout<<endl;//注意输出格式 
}
return 0;
}

代码抽风还请指证,有啥直接告诉我,我家就住在CSDN

例题6-3 UVa442

构造函数的使用

(**其实我是才学过,但是没用过,也不知道怎么用 **)
废话不多说上代码(题目通道)
紫薯总览——AC代码+小题解_第2张图片
图片大小调不了吗?

#include
#include
#include
#include
#include
#include  
using namespace std;
#define mid   100007
typedef long long ll;
//还是紫薯上的题
//还是用栈实现
/*思路就是字母也入栈,一遇到“)”就进行判断 
判断此时顶端的两个乘,如果不能乘,就直接错误,输出error
否则,sum加上,然后把两个相乘后矩形重新放入栈,其实整个过程
没有“()”入栈的情况,嘿嘿
思路清晰,不愧是大佬*/ 
struct www{
	int hang;
	int lie;
	www(int hang=0,int lie=0):hang(hang),lie(lie){}//这个就是精华,我见都没见过,后面有大作用 
	//猛男醒悟,这就是我才学的构造函数,居然结构体内也可以用,我还一直以为没有用 
}c[30];//记录矩阵 
int main(){
	int a,i,sum,t;
	char b;
	cin>>a;
	while(a--){//朴素的输入 
		cin>>b;
		cin>>c[b-'A'].hang>>c[b-'A'].lie; 
	}
	string s;
	stack<www>k;//建立栈 
	while(cin>>s){
		sum=0,t=0;
		for(i=0;i<s.size();i++){
			if(isalpha(s[i]))k.push(c[s[i]-'A']);//是字母就入栈
			else if(s[i]==')'){//"("没有用,牛逼,大佬 
			//我看紫薯上的程序也没判断出栈时元素不足两个的情况 
			www p=k.top();k.pop();
			www q=k.top();k.pop();//取出最上面两个
			if(q.lie!=p.hang){t=1;break;}//注意顺序 
			else{
			sum+=q.hang*q.lie*p.lie;
			k.push(www(q.hang,p.lie));/*这就是精华,怎样通过,q.a,p.b
			再放入栈还*/ 
			}
		}
	}
	if(t==1)cout<<"error"<<endl;
	else cout<<sum<<endl;
}
return 0;
}

我写代码真是一点都不好看
罪过罪过

慢着,我好像明白了这个构造函数
它其实就是用传进去的两个参数重新构造了一个www变量
字母只是一个表壳,内在就是这个www变量
ohhhhhhhhhhhhhhhhhh

6.2链表

例题6-4 UVa11988

本来使用链表,
可我不想用
其实是我看了好久,都想不通
就屈服于STL的伟大,用list
感觉确实有点慢,链表实现我下次补。
下次一定
血池

#include
#include
#include
#include
#include
#include  
using namespace std;
#define mid   100007
typedef long long ll;
//还是紫薯上的题
//无奈本人太菜鸡,这个链表怎么也想不通
//就先放过自己,用list
//早知道就用这个了,又浪费傻子几小时 
int main(){
	int i;
	string s;
	while(cin>>s){
		list<char>w;
		list<char>::iterator it =w.begin();
		for(i=0;i<s.size();i++){
			if(s[i]=='['){
				it=w.begin();//从前面 
			}
			else if(s[i]==']'){
				it=w.end();//从后面 
			}
			else{//进入list 
				w.insert(it,s[i]); 
			}
		}
		for(it=w.begin();it!=w.end();it++)cout<<*it;
		cout<<endl;
	}
	return 0;
}

对不住了。我看了一下下面一道链表,好像更难
只有先行略过了
菜鸡跳题,毫不犹豫
假装**第四滴血**

例题6-5 UVa12657

6.3树和二叉树

例题6-6 UVa679

大树
周树人
“抓捕周树人和我鲁迅有什么关系”

小球下落

#include
#include
#include
#include
#include
#include  
using namespace std;
#define mid   100007
typedef long long ll;
/*
//二叉树,简单模拟
int c[1<<20];//1向左移动20位就相当于pow(2,20); 
//最大节点个数[(1<<20)-1] 
int main(){
	int a,b,k;
	while(scanf("%d%d",&a,&b)==2){
		memset(c,0,sizeof(c));//开关清零
		int maxmax=(1<maxmax)break; 
			}
		}
		//出界之前的位置 
		cout< 
//上面的会超时,应为他每一个小球都模拟了,浪费了大量时间 
//其实我们只用知道小球的编号就可与以知道他的路径
//当在1节点时,编号为偶数,就会走右边,否则走左边
//每个节点都是这个道理 
int main(){
	int a,b,k,p;
	cin>>p;
	while(p--){
		cin>>a>>b;
		k=1;
		a--;//只能下降a-1次 
		while(a--){
			if(b%2==1){
				k*=2;//左树比右树多走一个 ,因为每次先走左数 
				b=(b+1)/2;
			}
			else{
				k=k*2+1;
				b/=2;
			}
		}
		cout<<k<<endl; 
	}
	cin>>p;
	return 0;
} 

例题6-9 UVa839

前面跳了两道题,
都是构造树的?没看明白
**菜鸡跳题,毫不犹豫 **
跳的两道题
第一道
第二道
紫薯总览——AC代码+小题解_第3张图片

紫薯的思路真的好清晰

#include
#include
#include
#include
#include
#include  
using namespace std;
#define mid   100007
typedef long long ll;
//紫薯继续 
//看题是递归输入,就是个模拟 
bool sou(int &k){//才学的引用 ,k值可以进行修改	
	int e,f,g,h; 
	bool p=true; 
	bool q=true; 
	cin>>e>>f>>g>>h;
	if(e==0){//左手是个子树 
		p=sou(e);//记录左边的重量 ,看左边是不是平衡的 
	}
	if(g==0){
		q=sou(g);//记录右边的重量 ,看右边是不是平横的 
	}
	k=e+g;
	return q&&p&&f*e==g*h;//左右和自己都平衡才是平衡 
}
int main(){
	int a,k=0;
	cin>>a;
	while(a--){
		if(sou(k)) 
		cout<<"YES"<<endl;
		else
		cout<<"NO"<<endl;
		if(a)cout<<endl;
	}
	return 0;
} 

例题6-10 UVa699

有两个小坑点就是
1.函数记得要有返回值
2.UVA的题对于输出格式好像很严格,行尾有空格都不行
紫薯上就没有没有,导致一直RE
题目传送门

#include
#include
#include
#include
#include
#include  
using namespace std;
#define mid   100007
typedef long long ll;
//紫薯继续 
//多用bool类型
int c[200]; 
//储存每个水平面的值
void jian(int a){
	int b;
	cin>>b;
	if(b!=-1)
	c[a]+=b;
	else
	return ;//等于-1就是没有路了,直接返回 
	jian(a-1);//先左 
	jian(a+1);//后右 
}
bool du(){//对第一个节点进行特殊处理
	int a;
	cin>>a;
	if(a==-1)
	return false;
	memset(c,0,sizeof(c));//因为多组输入 
	c[200/2]=a;//第一个节点处于最中间的位置 
	jian(200/2-1);//先左 
	jian(200/2+1);//后右 
	return true;
} 
int main(){
	int t=1;
	while(du()){
		int i=0;
		while(c[i]==0)i++;
		cout<<"Case "<<t++<<":"<<endl<<c[i++];
		while(c[i]!=0)cout<<" "<<c[i++];
		cout<<endl<<endl; 
	}
	return 0;
} 

6.4图

例题6-15 UVa10305

Kahn算法实现

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007  
typedef  long long ll;
// 紫薯继续(和平精英千年老二)
//拓扑排序
int du[106],a,b;//记录每个点的入度
vector<int>c[106];//邻接表
int ans[106];//记录答案
void sou(){
	int k=0,i;
	queue<int>p;
	for(i=1;i<=a;i++)//寻找出度为0的点
	if(du[i]==0)
	p.push(i);/*放进队列,因为每次
	开始处理都是从入度为0点开始*/ 
	while(!p.empty()){
		int xin=p.front();
		p.pop();
		ans[++k]=xin;//记录答案
		/*上面清除了一个入度为0的点,
		下面要把与之所相连的得点入度给剪掉 */
		for(i=0;i<c[xin].size();i++){
			du[c[xin][i]]--;
			if(du[c[xin][i]]==0){
			/*如果这时候产生一个新的
			入度为0 的点 */ 
				p.push(c[xin][i]); 
			}
		} 
	} 
	for(i=1;i<=a;i++){
		if(i==1)
		cout<<ans[i];
		else
	cout<<" "<<ans[i];
	}
	cout<<endl;
//	if(k!=a)//说明这个环
//	 return 0;
//	 else
//	 return 1;	
} 
int main(){
	int e,f;
	while(scanf("%d%d",&a,&b)==2){
		if(a==b&&b==0)
		break;
		for(int i=1;i<=a;i++){
			c[i].clear();//临接表清空 
		}
		memset(du,0,sizeof(du)); 
		while(b--){
			cin>>e>>f;
		du[f]++;//先完成e,才能再完成f
		c[e].push_back(f);//e->f 
	}
	sou();
	}
	return 0;
}

也可以DFS实现

例题6-16 UVa10129

欧拉回路
本题思路

//用并查集实现
//1.要查询每个点的祖先,有多个祖先的就无法形成通路(判断通路)
//2.有两个点的出度与入度不相等,且相差1。或者每个点出度入度都等
例如 asdsdb bsadjasjda

这个UVa今天交不上去啊
先把代码挂在这里

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007  
typedef  long long ll;
// 紫薯继续(和平精英千年老二)
//把单词看成有向边,单词开头末尾看作节点
//用并查集实现
//1.要查询每个点的祖先,有多个祖先的就无法形成通路(判断通路)
//2.有两个点的出度与入度不相等,且相差1
int f[30];//记录祖先节点 
int ru[30],chu[30],book[30]; 
int zu(int a){//寻找祖先节点 
	if(a!=f[a])
	return f[a]=zu(f[a]);
	else
	return  a; 
}
void chushihua(){
	memset(ru,0,sizeof(ru));
	memset(chu,0,sizeof(chu));
	memset(book,0,sizeof(book));
		for(int i=1;i<=26;i++){
			f[i]=i;
		}
}
int main(){
	int a,b,i,ans,r,ch,t;
	cin>>a;
	while(a--){
		r=ch=t=ans=0; 
		chushihua();
		cin>>b;
		while(b--){
			string s;
			cin>>s;
			int p=s[0]-'a'+1;
			int q=s[s.size()-1]-'a'+1;
			ru[p]++;
			chu[q]++;
			int w=zu(p);
			int z=zu(q);
			if(w!=z)
			f[z]=w;//合并 
			book[p]=book[q]=1;//记录有这个点 
		}
		for(i=1;i<=26;i++){
			if(book[i]){
				/*看看有几个祖先节点,
				只有一个才能形成通路*/
				if(f[i]==i)
				ans++;//祖先节点+1
				if(ru[i]!=chu[i]){
					if(ru[i]-chu[i]==1)
					r++;
					else if(chu[i]-ru[i]==1)
					ch++;
					//合法的出入度不同就这两种,其他都不正确 
					else
					t=1; 
				}
				//判段
				if(t||ans>1){
					break;
				}
			}
		}
		if(i!=27)
		cout<<"The door cannot be opened."<<endl;
		else
		{
		if((r==1&&ch==1)||(r==ch&&r==0))
		cout<<"Ordering is possible."<<endl; 
		else
		cout<<"The door cannot be opened."<<endl;
		}
	}
}

第七讲——暴力枚举

7.1简单枚举

例题7-1 UVa725

只用枚举前5个数,后五个数会自动形成
最后再判断一下数字是否重复

前排提示:UVA对输出格式有控制,WA死我了

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007  
typedef  long long ll;
// 紫薯继续(和平精英千年老二)
//暴力也需要优化!
/*直接枚举分子就行,
分母也可以得出,
最后看看有没有重复就行*/ 
int book[12],a,sum=0,book1[12],t;//两个book,标记两个数 
void sou(int b){
	if(b==6)
	{
		if(sum%a!=0)
		return ;//找不到整数被除数
		int k=sum/a;
		int q=k;/*记录这个数,
		因为后面判段数字是否重复时会改变 
		如果判断没有错误,就可以直接输出*/ 
		//分解k,看是否有元素重合
		memset(book1,0,sizeof(book1)); 
		for(int i=1;i<=5;i++){
			if(book[k%10]==1||book1[k%10]==1)//出现过了 
			return ;
			book1[k%10]=1; 
			k/=10;
		}
		t++;
		printf("%05d / %05d = %d\n",sum,q,a);
		return ;
	}
	//枚举 
	for(int i=0;i<=9;i++){
		int p=sum;
		if(book[i]==0){
			sum=sum*10+i;
			book[i]=1;
			sou(b+1);
			sum=p;
			book[i]=0;
		}
		//恢复原来状态
	}
}
int main(){
	int w=0;//执行的次数,因为最后输出不带\n 
	while(scanf("%d",&a)&&a){
		if(w)
		cout<<endl;
		w++; 
		memset(book,0,sizeof(book));
		t=0;
		sou(1);
		if(t==0)
		printf("There are no solutions for %d.\n",a);
	}
	return 0;
}

例题7-2 UVa11059

数据比较小,也就是个枚举
枚举起点和终点
终点动态变换,起点固定
两重循环就可以了

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007  
typedef  long long ll;
// 紫薯继续(和平精英千年老二)
//就是枚举,没别的 
ll c[20];
int main(){
	ll a,i,j,b;
	ll sum,maxmax,k=0,p;
	while(scanf("%lld",&a)!=EOF){
		maxmax=0;
		for(i=1;i<=a;i++)
		scanf("%lld",&c[i])A;
		for(i=1;i<=a;i++){//起点 
		sum=1;
			for(j=i;j<=a;j++){//终点移动 
				sum*=c[j];
				maxmax=max(sum,maxmax);
			}
		}
		 printf("Case #%d: The maximum product is %lld.\n\n", ++k, maxmax);
	}
	return 0;
} 

例题7-3 UVa10976

数学分析走着
解不等式
x=>2a>=y

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007  
typedef  long long ll;
// 紫薯继续(和平精英千年老二)
//数学题?
//解不等式——解出   x=>2a>=y 
int c1[10000];
int main(){
	int a,y,p;
	while(scanf("%d",&a)!=EOF){
		p=0;
		for(y=1;y<=2*a;y++){//y的取值范围
		//式子变换——a=xy/(x+y) 
		//x=ay/(y-a) 
		if(y-a>0&&(a*y)%(y-a)==0&&(a*y)/(y-a)!=0){
			c1[++p]=y;//记录答案
		}
		}
		cout<<p<<endl;
		for(int i=1;i<=p;i++)
		printf("1/%d = 1/%d + 1/%d\n",a,(a*c1[i])/(c1[i]-a),c1[i]); 
	}
	return 0;
} 

7.1回溯法

例题7-4 UVa524

前排恭喜vj上UVa连炸两天

素数环
回溯法
记住最后还要和环首 1 进行判断

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007  
typedef  long long ll;
//素数处理 
int book[20];//标记数组 
int ans[20];//记录答案
int su[50];//记录素数
int a; 
void sushu(){
	for(int i=1;i<=50;i++){
		for(int j=2;j*j<=i;j++){
			if(i%j==0){
				su[i]=1;//素数标记为0
				break; 
			}
		}
	}
}
void sou(int b){
	if(b==a+1){
	if(su[ans[b-1]+1]==0){//因为是环状,所以还要与开头 1 判断
	 for(int i=1;i<=a;i++){
	 	if(i!=a)//注意输入输出格式
	 cout<<ans[i]<<" ";
	 else
	 cout<<ans[i];
	}
	cout<<endl;
	return ;
	}
	}
	for(int i=2;i<=a;i++){
		if(book[i]==0&&su[i+ans[b-1]]==0){
			book[i]=1;
			ans[b]=i;
			sou(b+1);
			//回溯
			book[i]=0; 
		}
	}
}
int main(){
	int k=0;
	sushu();
	while(scanf("%d",&a)!=EOF){
		if(k!=0)
		cout<<endl;//注意输入输出格式 
		printf("Case %d:\n",++k);
		//第一个数为 1
		ans[1]=1;
		sou(2);
	}
}

例题7-5 UVa129

UVa的输入输出格式是真的难搞

注意子串相等时是怎样比较的
还是回溯法
每次添加一个字母进入字符串,最终顺序就是升序
我有点写不明白,还是自己看吧

紫薯上的一句话:
回溯法中,
应注意不必要的判断,
就像八皇后,每次只用判断新皇和之前的皇是否冲突,而不用判断以前的皇后是否冲突,因为以前已经判断过了

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;  
typedef  long long ll;
//这紫薯看的我天天喊牛皮
//回溯的思想
//每次添加一个字母
int w=0;//记录第几个困难串
int a,b,t;
char ans[100];//记录答案 
int jian(int c){
	int mid=c/2;//枚举比较的字串长度
	int i,j,p;
	for( i=1;i<=mid;i++){
		for( j=c+1-2*i,p=c+1-i;p<=c;j++,p++){//两个字串比较是否相等 
			if(ans[j]!=ans[p])//字串不相等 
			break; 
		}
		if(p==c+1)//字串相等
		return 0; 
	}
	return 1; 
}
void dfs(int c){
	if(t==1)
	return ; 
	if(++w==a){/*每次只要进行了dfs函数都会
	产生一个新的排列
	所以w++;*/ 
			t=1;
			//令人烦躁的输出
			for(int i=1;i<c;i++){
				cout<<ans[i]; 
				if(i%4==0&&i!=c-1){
					if(i%64==0)
					cout<<endl;
					else
					cout<<" ";
				}
				
			}
			cout<<endl<<c-1<<endl;
		return ;
	}
	for(int i=0;i<b;i++){
		ans[c]=i+'A';
		if(jian(c))//如果检查没有重复,就继续下一个
		dfs(c+1); //只要继续dfs,就是产生了一个排列 
		//否则就重新选择 
	}
} 
int main(){
	while(scanf("%d%d",&a,&b)&&b&&a){//平平无奇的输入 
		w=-1;
		t=0;
		dfs(1);
	}
	return 0;
} 

例题7-6 UVa140

例题7-7 UVa1354

这两个题我愣是没读懂题

7.5 路径寻找问题

例题7-8 UVa1354

烦躁

代码是我抄的,加了点解释
看着比紫薯上好懂
虽然长了点,但有一大段“重复的”

#include 
#include 
#include 
#include 
/*看紫薯的题解有点抽风,我的木鱼脑袋接受不了
找的其他题解 */  
using namespace std;
 
const int MAXN = 200;
 
int a, b, c, d, maxd1, minamount;
bool notvist[MAXN+1][MAXN+1][MAXN+1];
 
struct node {//记录每一个状态
    int a, b, c, amount;
    bool operator < (const node& n) const {
        return amount > n.amount;
    }//这个是干嘛的??????????????????
};
 
int bfs()
{
    maxd1 = 0;
    minamount = 0;
 	//因为是对移动水最少的进行扩展,所以使用优先队列 
    priority_queue<node> q;
 	//多组输入,标记数组清零 
    memset(notvist, true, sizeof(notvist));
 
    node f, v;
    f.c = c;
    f.a = 0;
    f.b = 0;
    f.amount=0;
    //标记初始状态 
    q.push(f);
 
    notvist[f.c][f.a][f.b] = false;
 	//下面进行扩展
    while(!q.empty()) {
        f = q.top();//优先队列这里是top
        q.pop();
        if(f.a == d || f.b == d || f.c == d)
            return f.amount;
 	//找到结果,直接退出dfs
        if(f.a < d && f.a > maxd1) {
            maxd1 = f.a;
            minamount = f.amount;
        }
        if(f.b < d && f.b > maxd1) {
            maxd1 = f.b;
            minamount = f.amount;
        }
        if(f.c < d && f.c > maxd1) {
            maxd1 = f.c;
            minamount = f.amount;
        }
 		/*用三个if寻找倒在杯子里
		的不大于h的最大值
		顺便记录步数 */
		
		// 下面枚举6中操作,大同小异
	 	//用v记录操作后的状态 
        // c --> a
        if(f.c && a - f.a > 0) {
            if(f.c > a - f.a) {         // c > a的剩余容量
                v.c = f.c - (a - f.a);
                v.a = a;
                v.b = f.b;
                v.amount = f.amount + (a - f.a);
            } else {                    // c <= a的剩余容量
                v.c = 0;
                v.a = f.a + f.c;
                v.b = f.b;
                v.amount = f.amount + f.c;
            }
            if(notvist[v.c][v.a][v.b]) {
                notvist[v.c][v.a][v.b] = false;
                q.push(v);
            }
        }
        // c --> b
        if(f.c && b - f.b > 0) {
            if(f.c > b - f.b) {         // c > b的剩余容量
                v.c = f.c - (b - f.b);
                v.a = f.a;
                v.b = b;
                v.amount = f.amount + (b - f.b);
            } else {                    // c <= b的剩余容量
                v.c = 0;
                v.a = f.a;
                v.b = f.b + f.c;
                v.amount = f.amount + f.c;
            }
            if(notvist[v.c][v.a][v.b]) {
                notvist[v.c][v.a][v.b] = false;
                q.push(v);
            }
        }
        // a --> c
        if(f.a && c - f.c > 0) {
            if(f.a > c - f.c) {         // a > c的剩余容量
                v.c = c;
                v.a = f.a - (c - f.c);
                v.b = f.b;
                v.amount = f.amount + (c - f.c);
            } else {                    // a <= c的剩余容量
                v.c = f.c + f.a;
                v.a = 0;
                v.b = f.b;
                v.amount = f.amount + f.a;
            }
            if(notvist[v.c][v.a][v.b]) {
                notvist[v.c][v.a][v.b] = false;
                q.push(v);
            }
        }
        // a --> b
        if(f.a && b - f.b > 0) {
            if(f.a > b - f.b) {         // a > b的剩余容量
                v.c = f.c;
                v.a = f.a - (b - f.b);
                v.b = b;
                v.amount = f.amount + (b - f.b);
            } else {                    // a <= b的剩余容量
                v.c = f.c;
                v.a = 0;
                v.b = f.b + f.a;
                v.amount = f.amount + f.a;
            }
            if(notvist[v.c][v.a][v.b]) {
                notvist[v.c][v.a][v.b] = false;
                q.push(v);
            }
        }
        // b --> c
        if(f.b && c - f.c > 0) {
            if(f.b > c - f.c) {         // b > c的剩余容量
                v.c = c;
                v.a = f.a;
                v.b = f.b - (c - f.c);
                v.amount = f.amount + (c - f.c);
            } else {                    // b <= c的剩余容量
                v.c = f.c + f.b;
                v.a = f.a;
                v.b = 0;
                v.amount = f.amount + f.b;
            }
            if(notvist[v.c][v.a][v.b]) {
                notvist[v.c][v.a][v.b] = false;
                q.push(v);
            }
        }
        // b --> a
        if(f.b && a - f.a > 0) {
            if(f.b > a - f.a) {         // b > a的剩余容量
                v.c = f.c;
                v.a = a;
                v.b = f.b - (a - f.a);
                v.amount = f.amount + (a - f.a);
            } else {                    // b <= a的剩余容量
                v.c = f.c;
                v.a = f.a + f.b;
                v.b = 0;
                v.amount = f.amount + f.b;
            }
            if(notvist[v.c][v.a][v.b]) {
                notvist[v.c][v.a][v.b] = false;
                q.push(v);
            }
        }
    }
    return -1;
}
 
int main()
{
    int t;
 
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d%d", &a, &b, &c, &d);
 
        int ans = bfs();
 
        if(ans < 0) {
            printf("%d %d\n", minamount, maxd1);
        } else
            printf("%d %d\n", ans, d);
    }
 
    return 0;
}

例题7-9 UVa1601

双向BFS
不会

7.6 迭代加深搜索

例题7-9 UVa1601

真的佩服看了大量题解看不懂,突然有个大佬的题解明白了
在线跪
牛逼

总结一下
这是那个大佬的题解链接

什么狗屁迭代加深搜索,看的我懵逼了一早上
这个也没涉及什么算法,所以看了还是好懂,但是叫我自己写,肯定写不出来
题目最重要的是每次搜索规定了上下界
还有好多小细节:多用乘法代替除法…
大佬真强

我改了一些细节

#include
#include
#include
#include
#include
#include
//真的是难,我要吐了
//真的是半天一道题,还是看的别人的代码
//大佬思路 
using namespace std;
const int N=1e5+7;
set <long long> p;
long long t,A,B,n,mxdep;
long long ans[N],w[N];
bool flag;
//inline long long gcd(long long a,long long b)
//{
//    return b ? gcd(b,a%b) : a;
//}//gcd是为了约分
inline bool pd()
{
    for(int i=mxdep;i;i--)
        if(ans[i]!=w[i])
            return ans[i]&&ans[i]<w[i];
    return 0;
}//判断答案是否更优
inline void dfs(long long dep,long long a,long long b,long long mi)
{
    if(dep==mxdep)//还剩最后一个数时 
    {
        if(b%a||(b*mi<a)||p.count(b/a)) return;//判断合法性
        //不合法 
	  /*判断合法性的三个条件 
	  1.可以构成1/n形式
	  2.比mi小
	  3.b/a这个数可以使用
	  */ 
	  //如果合法还要看是否为最优情况
        w[dep]=b/a;
        if(pd()) return;//判断答案是否更优
        memcpy(ans,w,sizeof(w)); flag=1;//更新
        return;
    }
    mi=max(mi,b/a+1);//求出下界
    for(long long i=mi;i;i++)
    {
        if( (mxdep-dep+1)*b<=i*a ) return;
        /*因为每个选择的
		数其实大小是递减的,如果这个数和以后的数
		分母都用i的和都比a/b小,那就肯定实现不了
		后面更大的i就更不用再考虑了*/
		/*用乘法表示的的优势在于不用
		害怕除法的精度丢失*/
        if(p.count(i)) continue;//判断合法
        w[dep]=i;//记录路径
        long long xa=a*i-b,xb=i*b;//通分
//        long long z=gcd(xa,xb);
        dfs(dep+1,xa,xb,i+1);//i改变因为i是递增的
    }
}
int main()
{
    cin>>t;
    for(long long i=1;i<=t;i++)
    {
        flag=0; mxdep=1;
        cin>>A>>B>>n;
        long long c;
        p.clear();//细节
        while(n--) scanf("%lld",&c),p.insert(c);//存储不合法的数
        while(mxdep++)//分裂成k个数就可以找到答案,从2开始
        {
            memset(ans,0,sizeof(ans));
            dfs(1,A,B,B/A+1);
            if(flag) break;
        }//迭代加深
        printf("Case %lld: %lld/%lld=1/%lld",i,A,B,ans[1]);
        for(int i=2;i<=mxdep;i++)
            printf("+1/%lld",ans[i]);
        printf("\n");
    }
    return 0;
}

例题7-10 UVa11212

什么IDA算法了,过

第八讲——高效算法设计

what??????????????

第九讲——动态规划初步

9.1 数字三角形

9.1.1数字三角形

数字三角形

标准的动态规划,容易实现

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007
int a,ans[102][102],c[102][102];
int sou(int x,int y){
	if(x==a){//最后一层 
		return ans[x][y]=c[x][y];
	}
	else{
		if(ans[x][y]!=0)
		return ans[x][y];
		else{
			return ans[x][y]=c[x][y]+max(sou(x+1,y),sou(x+1,y+1));
		}
	}
}
int main(){
	cin>>a;
	for(int i=1;i<=a;i++){
		for(int j=1;j<=i;j++){
			cin>>c[i][j];
		}
	}
	cout<<sou(1,1);
}

9.2 DAG上的动态规划

9.2.1DAG模型

矩形嵌套问题

有向无环图,不清楚DAG的可以博客查一下
感觉和上一道数字三角形差不多
就是
1.记忆化
2.建立有向图
3.保证每个点都有ans值
4.详细的写在代码里了

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define mid 1000000007
//有向无环图
int b,ans[1006],xian[1006][1006],c[1006][2];
int dp(int a){
	int i,maxmax;
	if(ans[a]>0)
	return ans[a];//记忆化 
		maxmax=1;//注意初值设置为1 
		for(i=1;i<=b;i++){
			if(xian[a][i])//如果有向边存在 
				maxmax=max(dp(i)+1,maxmax);
		}
		return ans[a]=maxmax;
}
int main(){
	int a,maxmax,i,j;
	cin>>a;
	while(a--){
		memset(ans,0,sizeof(ans));
		memset(xian,0,sizeof(xian)); 
		cin>>b;
		for(i=1;i<=b;i++)cin>>c[i][0]>>c[i][1];
		//建立有向图
		for(i=1;i<=b;i++){
			for(j=1;j<=b;j++){
				if((c[i][0]>c[j][0]&&c[i][1]>c[j][1])||(c[i][1]>c[j][0]&&c[i][0]>c[j][1])){
					xian[i][j]=1;//注意是j可以放在i里面 
				}
			}
		}
		maxmax=-1;
		//注意下面这样做的理由是
		//让每一个点肯定都会得到ans 
		for(i=1;i<=b;i++){
			maxmax=max(maxmax,dp(i));//找出最大的那个ans 
		}
		cout<<maxmax<<endl;
	}
	return 0;
} 

硬币问题

这题我都找了好久

不用引用也能错?我吐了
代码是我网上cv的,我的有个样例超时,恶心死我

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int k = 0x3f3f3f3f;//最大最小值
int ans[10006], c[106], book[10006], a, b;//book数组用来记忆化
int da(int x) {
    if (book[x])
        return ans[x];
    book[x] = 1;
    int &maxmax = ans[x];//不用引用就完蛋
    maxmax = -k;
    for (int i = 1; i <= a; i++) {
        if (x >= c[i]) {
            maxmax = max(maxmax, da(x - c[i]) + 1);
        }
    }
    return ans[x] = maxmax;
}
int xiao(int x) {
    if (book[x])
        return ans[x];
    book[x] = 1;
    int &minmin = ans[x];
    minmin = k;
    for (int i = 1; i <= a; i++) {
        if (x >= c[i]) {
            minmin = min(minmin, xiao(x - c[i]) + 1);
        }
    }
    return minmin;
}
int main() {
    cin >> a >> b;
    for (int i = 1; i <= a; i++) {
        cin >> c[i];
    }
    book[0] = 1;
    ans[0] = 0;
    cout << xiao(b);
    memset(book, 0, sizeof(book));
    book[0] = 1;
    ans[0] = 0;
    cout << " " << da(b);
}

9.2.4 小结与应用举例

例题9.2 巴比伦塔

和前面的矩形嵌套大同小异

每个立方体产生三种摆放情况,然后就是标准的记忆化搜索


struct www{
	int x;int y;int z;
}c[200];//结构体储存每种摆放方式

int ans[200],a;

int dp(int p){//标准的记忆化搜索
	int i,maxmax;
	if(ans[p]){
		return ans[p];
	}
	maxmax=c[p].z;/*记住初始值设置为自己的长度,
	最短就是没有可以放在上面的,最小值就是自己*/
	for(i=1;i<=a*3;i++){
		if((c[i].x<c[p].x&&c[i].y<c[p].y)||(c[i].x<c[p].y&&c[i].y<c[p].x)){
			maxmax=max(maxmax,dp(i)+c[p].z);
		}
	}
	return ans[p]=maxmax;
}


int main(){
	int i,p,e,f,g,k=0;
	while(scanf("%d",&a)&&a){
		p=0;
		memset(ans,0,sizeof(ans));
		for(i=1;i<=a;i++){
		cin>>e>>f>>g;
		c[++p].x=e,c[p].y=f,c[p].z=g;
		c[++p].x=f,c[p].y=g,c[p].z=e;
		c[++p].x=g,c[p].y=e,c[p].z=f;
		//每个立方体三种摆放方式 
	}
	int maxmax=0;
		for(i=1;i<=a*3;i++){
			maxmax=max(dp(i),maxmax);
		}
		cout<<"Case "<<++k<<": maximum height = "<<maxmax<<endl;
	}
	return 0;
}

9.3 多阶段决策问题

9.3.1 多段图的最短路

例题9.4 单项TSP

就是一个DP
注意细节的处理
代码又是一直WA

9.3.2 0-1背包问题

物品无限的背包问题(完全背包)

带权值的dp?就是以前dp的+1变成了+实质的值

int c[103][2], a, b;
int dp[50006];
int sou(int x);
int main() {
	memset(dp, -1, sizeof(dp));
	cin >> a >> b;
	for (int i = 0; i < a; i++) {
		cin >> c[i][0] >> c[i][1];
	}
	cout<<sou(b);
	return 0;
}
int sou(int x) {
	if (dp[x] != -1)
		return dp[x];
	int maxmax = 0;
	for (int i = 0; i < a; i++) {
		if (x >= c[i][0]) {
			maxmax = max(maxmax, c[i][1] + sou(x - c[i][0]));
		}
	}
	return dp[x] = maxmax;//记忆化
}

0-1背包问题

典型的0-1背包
代码是看了别人的,感觉还是没法独立写出来

int a, b, c[101][2],ans[10006];
int main() {
	cin >> a >> b;
	for (int i = 1; i <= a; i++) {
		cin >> c[i][0] >> c[i][1];
	}
	for (int i = 1; i <= a; i++) {
		for (int j = b; j >=c[i][0]; j--) {//保证可以往包里拿
			ans[j] = max(ans[j],ans[j-c[i][0]]+c[i][1]);//加上前面的效果
		}
	}
	cout << ans[b];
}

例题9.5 劲歌金曲

可算AC了

也算典型的01背包问题了,时间其实最大是有范围的。
注意最后要留一秒才能唱金曲,详细解释写在代码里了

#include
#include
#include
#include
#include
#include
#include
using namespace std;
//01背包
int k[52];
int ans[10000];//时间是有范围的
int main() {
	int a, b, c, z = 0;//z是输出时用的
	cin >> a;
	while (a--) {
		memset(ans, -1, sizeof(ans));
		cin >> b >> c;
		for (int j = 1; j <= b; j++) {
			cin >> k[j];
		}//平凡的输入
		ans[0] = 0;
		for (int i = 1; i <= b; i++) {//物品
			for (int j = c; j >=k[i]; j--) {//时间
				if (ans[j-k[i]] != -1)//确保j-k[i]在这个时间唱歌有可能实现
				ans[j] = max(ans[j], ans[j - k[i]] + 1);
			}
		}
		int maxmax = -1, p ;
		for (int i = 0; i < c; i++) {//等于c的情况特殊处理,因为c时无法唱《金曲》
			if (ans[i] >=maxmax) {//找最大的ans,并且时间i也要大的,所以有等号
				maxmax = ans[i];
				p = i;
			}
		}
		if (maxmax + 1 > ans[c] || (maxmax + 1 == ans[c]&&p+678>c))
	//条件判断
			printf("Case %d: %d %d\n", ++z, maxmax + 1, p + 678);
		else
			printf("Case %d: %d %d\n", ++z, ans[c], c);
	 }
	return 0;
}

9.4 更多经典模型

9.4.1 线性结构上的动态规划

9.4.1 最长上升自序列(LIS)

只写了最智障的写法,其他的还不懂,后面再来

//最智障的n^2
int dp[1006],c[1006];
int main() {
	int a;
	cin >> a;
	for (int i = 1; i <= a; i++) {
		cin >> c[i];
		dp[i] = 1;
	}
		for (int i = 1; i <= a; i++) {
			for (int j = 1; j < i; j++) {
				if (c[i] > c[j]) {//比前面的数大,才可能形成上升序列
					dp[i] = max(dp[i], dp[j] + 1);
					/*表示以i结尾的最长上升序列,
					所以后面还有个遍历寻找最大*/
				}
			}
		}
	int ans = 0;
	for (int i = 0; i <= a; i++) {
		ans = max(ans, dp[i]);
	}
	cout << ans;
	return 0;
}

连环大跳(跳了很多)

第十讲——数据结构基础

10.1 数论初步

10.1.1 欧几里得算法和唯一分解定理

你可能感兴趣的:(紫薯紫薯,数据结构,链表,算法,c++)