算法竞赛入门竞赛 入门经典 第六章 个人记录

 

UVa 210 并行程序模拟(放弃 || 待补)

没看懂题意,但是有百度了一下duque 算是对duque有了一个大致的认识和了解 当然也有尝试。

本来想着去hdu找一些duque的题 结果 好像可以用上duque题目都比较高端 畏难而退orz

 

UVa 514 铁轨(待补)

再次败在题意

没懂 为什么 54321 可以Yes 而54123会是No

按照我的理解 54可以先在C(栈)暂存 然后 123各自进栈弹出 最后54弹出 变成12345

案例程序 可以大致读懂 但是发现程序结束的条件 会在栈中会有存留 直接判断No 然后……理解不了

目前找的博客都没有更详细的解释题意

打算在章节看的差不多的时候 回来补

 

UVa 442 矩阵链乘

巧妙之处:

1.用name[i]-'A' 作为下标

2.因为()成对出现 所以可以在出现)的时候直接弹出两个栈中元素 再将结果入栈

如书本分析所说 这一题最为关键的是解析式的分析 因为已经有分析所以代码读起来格外清楚

总之 自己打了一下 一遍AC 充分证明了分析解析式的重要性!

#include 
#include 
#include 
using namespace std;

struct node
{
	int a, b;
};

int main()
{
	int n;
	cin >> n;
	node m[26];
	for (int i = 0; i < n; i++)
	{
		string name;
		cin >> name;
		int k = name[0] - 'A';
		cin >> m[k].a >> m[k].b;
	}
	string expr;
	while (cin >> expr)
	{
		stacks;
		int flag = 0;
		int ans = 0;
		int len = expr.length();
		for (int i = 0; i < len; i++)
		{
			if (isalpha(expr[i]))s.push(m[expr[i] - 'A']);
			else if (expr[i] == ')')
			{
				node m2 = s.top(); s.pop();
				node m1 = s.top(); s.pop();
				if (m1.b != m2.a) { flag = 1; break; }
				ans += m1.a*m1.b*m2.b;
				node t;
				t.a = m1.a; t.b = m2.b;
				s.push(t);
			}
		}
		if (flag)
			cout << "error" << endl;
		else
			cout << ans << endl;
	}
	system("pause");
	return 0;
}

Q 为什么使用结构体数组 不使用map[name]这样的方法 ——9.13

A 不行 因为这样就没法检测出‘(‘ 真的一点办法都没有(我觉得……)——9.15 

UVa 11988 破损的键盘

  1. 试图使用STL的list做……发现 自己想的天真了……但是我觉得list肯定能做就是会比较繁琐……emmm能吗?
  2. 看不懂书本代码【开始烦恼 next数组的目的一点都不清楚
  3. 打算是输出的for循环入手 探究next的用途【计划通】
  4. next是用坐标链接坐标的方式表示链表 不同于指针表示的链表 不过……讲真的这样的链表方式我觉得抽象很多 传统的指针链表反而变得形象 来感觉的时候一定要用指针链表再来一次 也不同于PAT中翻转链表的一种做法 : 用结构体数组存储模拟的自身地址、数据、下个节点地址的方式……不过讲真我觉得这个方法好像也行得通……
  5. 这好像真的不是很难的题嘛……然鹅……orz
  6. 开始有点感觉:next[0]存放head 本质上next是存放输出顺序的数组 首先读入一行字符串 然后挨个遍历 判断是否为'['']' cur类似于临时指针 记录遍历的状况 0是头指针 last是尾指针 读到‘[’ cur保存为0 然后……最神奇的地方就出现了 next[i]=next[cur] 如果没有遇到'['也就是遇到第一个'[' (以第一个案例为例)之前 同样的代码 执行的操作是将读取到的字符放到链尾(因为cur与last相同)而在遇到'['后 又可以将之后的输入的插入到链头 我不知道该如何形容我现在的感受 就……很玄幻 就很神奇 可能指针链表也是这样的情况 可能源于对链表刚刚接触的不熟悉 但是 就很神奇……所以插入位置的关键在于指针cur
  7. 尝试自己打了遍代码
    #include 
    #include 
    using namespace std;
    
    const int maxn = 100000 + 5;
    
    int main()
    {
    	char s[maxn],next[maxn];
    	int cur,last;
    	while (cin >> s + 1)
    	{
    		cur = last = 0;
    		next[0] = 0;
    		int n = strlen(s+1);
    		for (int i = 1; i <= n; i++)
    		{
    			if (s[i] == '[')
    				cur = 0;
    			else if (s[i] == ']')
    				cur = last;
    			else
    			{
    				next[i] = next[cur];
    				next[cur] = i;
    				if (cur == last)last = i;
    				cur = i;
    			}
    		}
    		for (int i = next[0]; i != 0; i=next[i])
    		{
    			printf("%c", s[i]);
    		}
    		cout << endl;
    	}
    	system("pause");
    	return 0;
    }

    发现自己在理解上仍有不到位 很奇怪next[i]=next[cur] next[cur]=i …… 0很明显是一个界限 是结束的标志 然后next[0]赋值为0 接着就是0下移 然后下移的位置的序号赋值给原来的数组元素 是一个向后添加节点的过程 i是新增的节点 0是尾节点 如果 '['要在头上添加节点 那么next[0]中保存的序号一定会被赋值给next[i] 然后next[0]会等于i 然后这个链会依然维持一个链

  8. 至此 这题我觉得算是基本能读懂 明天将会再打几遍加深理解和印象 (2018/9/15 0:28)留下刻苦的时间以掩盖六点到九点都在打游戏的事实【然鹅……】

  9. 案例过了姑且就睡觉了 所以实际的提交时间是15号中午左右 错了一次 len=strlen(s+1)才是对的 不然会多一个空格 应该就是\0 然后这题姑且先告一段落 这种链表表示方法应该得记住

  10. 继续阅读书本

 

UVa 12657 移动盒子

死性不改 先用list整一遍

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 100000 + 5;

listl;
void p()
{
	list::iterator it;
	for (it = l.begin(); it != l.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int main()
{
//	freopen("D:\\in.txt", "r", stdin);
//	freopen("D:\\out.txt", "w", stdout);
	int n, m;
	int cas = 0;
	while (cin >> n >> m)
	{
		l.clear();
		for (int i = 1; i <= n; i++)
		{
			l.push_back(i);
		}
		while (m--)
		{
			int op;
			int x, y;
			cin >> op;
			if (op == 1)
			{
				cin >> x >> y;
				list::iterator itx,ity;
				itx = find(l.begin(), l.end(), x);
				l.erase(itx);
				ity = find(l.begin(), l.end(), y);
				l.insert(ity, x);
				//p();
			}
			else if (op == 2)
			{
				cin >> x >> y;
				list::iterator itx, ity;
				itx = find(l.begin(), l.end(), x);
				l.erase(itx);
				ity = find(l.begin(), l.end(), y);
				l.insert(++ity, x);
				//p();
			}
			else if (op == 3)
			{
				cin >> x >> y;
				list::iterator itx, ity;
				itx = find(l.begin(), l.end(), x);
				ity = find(l.begin(), l.end(), y);
				l.insert(ity, x);
				l.insert(itx, y);
				l.erase(ity);
				l.erase(itx);
				//p();
			}
			else if (op == 4)
			{
				l.reverse();
				//p();
			}
		}
		long long cnt = 1;
		long long ans = 0;
		for (list::iterator it = l.begin(); it != l.end(); it++)
		{
			if (cnt & 1)
				ans += *it;
			cnt++;
		}
		cout << "Case " << ++cas << ": " << ans << endl;
	}
//	fclose(stdin);
//	fclose(stdout);
//	system("pause");
	return 0;
}

TLE!0w0+

好的 老老实实看书

稍稍学着用了下list 顺便熟悉了iterator一下 哦对了 还有find函数 这个有爽到!

  1. 15日晚 第一次看题目 用list做了一遍 结果如上 = =|||(16日晚了解到超时的原因不是我原来所想的reserve的关系 而是find函数用的是顺序查找 撤回爽到的前言 - -#)
  2. 16日下午 开始看代码 并照着打了一遍
  3. 了解了书本代码的思路和方法
  4. 16晚开始尝试自己用书上的方法敲
  5. 开始对操作3的交换部分代码产生疑问 这就有一个让我很纠结的特例 因为x的右边是y 然后ry就也会是y 所以无法让自己和自己链接 但是为什么 x的左边是y就不用特判
  6. 百度其他人的博客 发现其他人也注意到了这个问题
  7. 反复纠结 决定暂定为书本代码遗漏 既然可以有反例 就可以解释
  8. 事实上 输出的是奇数位的总和 而且3不一定是最后一个指令 x和y也一定是相邻 
  9. 哦!!!我知道了!因为当3的时候早就特判过了!!不是漏洞 是我自作聪明!
  10. 这种方法的优点是快速 不用考虑太多的指针什么的 但是缺点也很明显 很容易出现特例 所以需要不少的特判 我觉得我会很容易遗漏 这道题目就需要特判四种情况?(五种?三种?)总之X 和 Y相邻的时候及其容易出问题 虽然如此 但是好巧妙真的非常牛 如果以后实战的时候可能真的施展 就真的是学到了
  11. 想用这种方法试试上一题 虽然没有必要用双向链表 但我觉得……思考后发现 我好像有点思维定式全是书本上这题的方法 感觉就不大对 可能过些天也好
#include 
#include 
using namespace std;

const int maxn = 100005;
int l[maxn],r[maxn];

void link(int a, int b)
{
	r[a] = b;
	l[b] = a;
	//即a的右边是b b的左边是a
}

void p()
{
	int b = 0;
	for (int i = r[b]; i != 0; i=r[i])
	{
		cout << i << " ";
	}
	cout << endl;
}

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int n, m;
	int kas = 0;
	while (cin >> n >> m)
	{
		for (int i = 1; i <= n; i++)
		{
			l[i] = i - 1;
			r[i] = (i + 1) % (n + 1);//为了使r[n]=0 r[i]=i==n?0:i+1;应该一样……?
		}
		l[0] = n; r[0] = 1;
		int f = 0;
		int x, y;
		int op;
		while (m--)
		{
			cin >> op;
			if (op == 4)f = !f;
			else
			{
				cin >> x >> y;
				//需要特判
				if (op == 3 && r[y] == x)swap(x, y);//操作3时 当X和Y相邻时就会出错 不论是 3 X Y 还是 3 Y X 为了后续操作简单 将3 Y X 也变成 3 X Y 归纳为一种情况
				if (op != 3 && f)op = 3 - op;
				if (op == 1 && x == l[y])continue;//即操作1时 x已经在y左边 需要忽略
				if (op == 2 && x == r[y])continue;//即操作2时 x已经在y右边 需要忽略

				int lx = l[x], rx = r[x], ly = l[y], ry = r[y];//其实这一行不定义直接用数组也行
				if (op == 1)
				{
					link(lx,rx);
					link(ly, x);
					link(x, y);
					//p();
				}
				else if (op == 2)
				{
					link(lx, rx);
					link(y, x);
					link(x, ry);
					//p();
				}
				else if (op == 3)
				{
					if (r[x] == y)
					{
						link(lx, y);
						link(y, x);
						link(x, ry);
					}
					else
					{
						link(lx, y);
						link(y, rx);
						link(ly, x);
						link(x, ry);
					}
				}
			}
		}
		long long ans = 0;
		int b = 0;
		for (int i = 1; i <= n; i++)
		{
			b = r[b];
			if (i % 2 == 1)ans += b;
		}
		if (f && n % 2 == 0)ans = (long long)n*(n + 1) / 2 - ans;//ans此时求的是偶数位和 也就是说我们只要用总和-偶数位和就可以得到奇数位和
			//如果是个奇数 那么倒置了 还是这些数 所以不用处理
		printf("Case %d: %lld\n", ++kas,ans);
	}
	//fclose(stdin);
	//fclose(stdout);
	//system("pause");
	return 0;
}

补了一半题 其他的好像有图论 也有数论 姑且也都暂时放下……orz 我觉得我真的是蠢的那种 讲真 能进元培 高考可以本科我就谢天谢地了 能进实验室更是梦寐以求的事情 现在这样高中时想都不敢想

我觉得我在用工了 当然我一定比不上人家 人家不用工有那点数学功底坐在那里看看直播 都能比我会做题 我想想如果我也向他一样我别说什么dfs什么链表 我都没法做 因为我很清楚自己的下限 我在进步我能感觉到 这样可能就够了 我不能太逼自己 我是人 会崩溃 而且因为该死的一些原因我会更容易奔溃我自己也清楚的很 所以 这样就可以了 我做的很好了 我只要保持就好了

……以上是我为我的懒惰与愚蠢找的借口……是这样么?

UVa 679 小球掉落

#include 
#include 
using namespace std;

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int n;
	while (cin >> n && n != -1)
	{
		while(n--)
		{
			int d, l;
			int k = 1;
			cin >> d >> l;
			for(int i=0;i

这题是属于有书本指导问题就不大的题目 超时的数组我没敲 感觉真要模拟自己应该有这个能力(真的吗……)

 

其实开始看的时候是有点问题的 应该是出于对数字和数据的不敏感 

但尝试着模拟小球的掉落后其实就很容易感受到 掉落到1点上的球会一左一右一左一右 那么归纳后就是奇数往左 偶数往右

然后就是书上所描述的 只要知道是掉落到 某节点的球 是遇到这个节点的第几个点就可以了

 

看了看uva 122 后 突然想 如果用指针建个树……可以吗……?不行怕是会超时 下次再试……

 

UVa 122 树的层次遍历

第一遍 照着书上的代码打了一遍  自己电脑报错了两次 wa了一次 wa是因为……算了不说了 自作聪明的原因……头疼……

经过两天重复的敲打(我觉得至少五遍 可能接近十遍 大多都会有这样那样的小问题 然后大部分可以自己调试出来 小部分是对照AC的和书上的代码找出来的 ps:后面的真是找的眼睛疼 这个故事告诉我们调试能力的重要性)重复敲我觉得还是有意义的 就一点点的理解会加深 然后调试可以搞出来的错误也能理解这样写为什么会错 以至于被自己蠢死……(不举例了 丢死人了)

#include 
#include 
#include 
#include 
using namespace std;

struct node
{
	int v;
	bool have_value;
	node *left;
	node *right;
	node() :have_value(false), left(NULL), right(NULL) {}
};

node *root;

node *newnode() { return new node(); }
void delnode(node *u)
{
	if (u == NULL)return;
	if (u->left)delnode(u->left);
	if (u->right)delnode(u->right);
	delete u;
}

bool failed;
char s[300];

void addnode(int v,char *s)
{
	int n = strlen(s);
	node *u = root;
	for (int i = 0; i < n; i++)
	{
		if (s[i] == 'L')
		{
			if (u->left == NULL)u->left = newnode();
			u = u->left;
		}
		if (s[i] == 'R')
		{
			if (u->right == NULL)u->right = newnode();
			u = u->right;
		}
	}
	if (u->have_value)failed = 1;
	u->have_value = 1;
	u->v = v;
}

bool read()
{
	failed = false;
	delnode(root);
	root = newnode();
	while (1)
	{
		if (scanf("%s", s) != 1)return false;
		if (!strcmp(s, "()"))break;
		int v;
		sscanf(&s[1], "%d", &v);
		addnode(v, strchr(s, ',') + 1);
	}
	return true;
}
bool bfs(vector&ans)
{
	ans.clear();
	queueq;
	q.push(root);
	while (q.size())
	{
		node *u = q.front(); q.pop();
		if (u->have_value==NULL)return false;
		ans.push_back(u->v);
		if (u->left)q.push(u->left);
		if (u->right)q.push(u->right);
	}
	return true;
}

int main()
{
	while (read())
	{
		vectorans;
		if (!bfs(ans))failed = true;
		if (failed)
			cout << "not complete" << endl;
		else
		{
			for (int i = 0; i < ans.size(); i++)
				cout << ans[i] << (i != ans.size()-1 ? " " : "\n");
		}
	}
	//system("pause");
	return 0;
}

不知道是不是我的错觉 我觉得二叉树比链表好写……

 

接下来将尝试换成数组……测试样例已过……然鹅WA了……

AC了……万万没想到是书上的代码有问题!(这次是真的有问题不是我自作聪明!!!)

#include 
#include 
#include 
#include 
using namespace std;

//struct node
//{
//	int v;
//	bool have_value;
//	node *left;
//	node *right;
//	node() :have_value(false), left(NULL), right(NULL) {}
//};

const int root = 1;
const int maxn = 10000;
int cnt;
int va[maxn];
int l[maxn], r[maxn];
bool have_value[maxn];

void newtree()
{
	l[root] = r[root] = 0;
	have_value[root] = false;
	cnt = root;
}

int nnode()
{
	int u = ++cnt;
	l[u] = r[u] = 0;
	have_value[u] = false;//书上是 have_value[root]=false 但是这就会导致have_false[root]即使是赋值了 之后给左树或者右树赋值后 根节点又会变成false也就是未赋值 然后就直接说这个树没有根节点……
	return u;
}

bool failed;
char s[300];

void addnode(int v, char *s)
{
	int n = strlen(s);
	int u = root;
	for (int i = 0; i < n; i++)
	{
		if (s[i] == 'L')
		{
			if (!l[u])l[u] = nnode();
			u = l[u];
		}
		if (s[i] == 'R')
		{
			if (!r[u])r[u] = nnode();
			u = r[u];
		}
	}
	if (have_value[u])failed = 1;
	have_value[u] = 1;
	va[u] = v;
}

bool read()
{
	failed = false;
	newtree();
	while (1)
	{
		if (scanf("%s", s) != 1)return false;
		if (!strcmp(s, "()"))break;
		int v;
		sscanf(&s[1], "%d", &v);
		addnode(v, strchr(s, ',') + 1);
	}
	return true;
}
bool bfs(vector&ans)
{
	ans.clear();
	queueq;
	q.push(root);
	while (q.size())
	{
		int u = q.front(); q.pop();
		if (have_value[u] == 0)return false;
		ans.push_back(va[u]);
		if (l[u])q.push(l[u]);
		if (r[u])q.push(r[u]);
	}
	return true;
}

int main()
{
	/*freopen("D:\\in.txt", "r", stdin);
	freopen("D:\\out.txt", "w", stdout);*/
	while (read())
	{
		vectorans;
		if (!bfs(ans))failed = true;
		if (failed)
			cout << "not complete" << endl;
		else
		{
			for (int i = 0; i < ans.size(); i++)
				cout << ans[i] << (i != ans.size() - 1 ? " " : "\n");
		}
	}
	/*fclose(stdin);
	fclose(stdout);*/
	//system("pause");
	return 0;
}

数组建树 实际上和两道链表题目 用的思路是一样的 数组的值作为令一个数组元素的下标 不管做几次都觉得神奇 很巧妙 而且有趣 很牛……

我对于变量多的题目梳理起来容易乱 然后这样就算把这道题过掉了 第三种指针+数组的方法目前不打算尝试了 鸽置了……

最后独立敲了一遍 调试了一下 感觉还行 掌握可能还说不上 但是有点感觉了 9-19 20:48

 

UVa 548 树

要死的勘误 而且勘误列表里没有 这个绝对是勘误 后序遍历第一个绝对不可能是根 事实上 最后一个才是 这是绝对是勘误 质疑!

 

好的 抱怨完了

先序遍历 根 左子树 右子树 也就是说 从根一路往左 到底了 回去上一个 往右 到底回去 如果左右两个都遍历了 再回去 这样类推

中序遍历 左子树 根 右子树 先到最左边最下边 然后 回去 再到右边 如果左右两个都遍历了 再回去 这样类推 

后序遍历 左子树 右子树 根 先到最左边最下边 然后 回去 再到右边 再回去根 然后再回去上一个根 往右 再到根 这样类推

然后这个题 就给了一个中序遍历访问的节点的序号(我就当他是序号吧)和一个后序遍历访问的节点的序号 要求一个叶子到根路过的节点序号之和最小 如果和相同 就输出叶子最小的叶子序号经过的路径和

 

18号晚上基本就纠结题意了 然后 看不懂样例 输入 这个树就构造不出来 即使是注意到了开头讲的最后一个值是根……

然后19号下午 就(心浮气躁地)调试了一下 画画了图 基本有了个概念 可以把树构造出来了 代码到19号晚上 也就是现在21:11 还没有动手开始敲过 这个效率可以说很低了

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 10000 + 10;

int l[maxn], r[maxn], in_order[maxn], post_order[maxn];
int n;

bool read(int *a)
{
	string line;
	if (!getline(cin, line))return false;
	stringstream ss(line);
	n = 0;
	int x;
	while (ss >> x)a[n++] = x;
	return n > 0;
}

int build(int L1, int R1, int L2, int R2)
{
	if (L1 > R1)return 0;
	int root = post_order[R2];
	int p = L1;
	while (in_order[p] != root)p++;
	int cnt = p - L1;
	l[root] = build(L1, p - 1, L2, L2 + cnt - 1);
	r[root] = build(p + 1, R1, L2 + cnt, R2 - 1);
	return root;
}

int best, best_sum;

void dfs(int u, int sum)
{
	sum += u;
	if(!l[u] && !r[u])
		if (sum < best_sum || (sum == best_sum && u < best))
		{
			best = u;
			best_sum = sum;
		}
	if (l[u])dfs(l[u], sum);
	if (r[u])dfs(r[u], sum);
}

int main()
{
	//freopen("D:\\in.txt","r",stdin);
	//freopen("D:\\out.txt", "w", stdout);
	vectorans;
	while (read(in_order) && read(post_order))
	{
		build(0, n - 1, 0, n - 1);
		best_sum = 1000000000;
		dfs(post_order[n - 1], 0);
		cout << best << endl;
	}
	//fclose(stdin);
	//fclose(stdout);
	//system("pause");
	return 0;
}

动手了……21:50

现在的疑惑的点在build函数 cnt的作用应该是记录子树的点的个数 传输进函数的数据也有疑问 L1,p-1 和 L2 到L2+cnt-1的数量应该是一样的 突然觉得有点分治的意思……明天再看吧……

20日 下午是没有课的 然后一觉睡到三点半orz……之后尝试打了一遍 基本可以理解了

l1-r1 为in的区间 l2-r2 为post的区间 为取到不同的子树 所以用不同的参数 
因为post的最后一个数字一定是根 所以可以获得根的值 再根据根在In中的位置确认左子树的成员 因为这是一个树的两个不同的遍历顺序 所以左子树的大小是一致的
所以可以通过对In中的左子树的数量来判断post中左子树的数量 
post 的最后一个数 确定根 再由in中根的位置 确定左子树和右子树 就是这样

书上也有……emmm 好辣 我知道我看书不认真了 自己琢磨肯定也会有好处的辣 下次我注意辣#
再有一个就是 l中 r2确定是需要l2+cnt-1的 因为下标是从0开始的 而r中l2的确认只需要cnt+l2 因为In的root在中央 而post的末尾

那么问题来了 build函数中l1,l2看似是没有变化的 那么 可以用最初传递进build函数的参数定义build函数吗

明显不能……也不知道我怎么想的 居然觉得能的……根→左子树的根→右子树 这样一来参数就一定会变化 所以怎么可能可以固定 

其实这个问题本身就很蠢……

 

然后是dfs dfs基本上没什么问题了 也基本可以理解了 原理就是递归到无法递归 然后retuen 需要思考的是结束的标志 和各种限制继续递归的条件 当初解除dfs和bfs的时候谁能想到这该死的东西居然是用来遍历的基本功=A=

 

打算再敲一遍 然后试着用指针建树……其实只要把数组改成指针就行了把……?

#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 10000 + 10;
int l[maxn], r[maxn];
int in_order[maxn], post_order[maxn];
int n;

int best_sum;
int best;

struct node
{
	int v;
	node *left;
	node *right;
	node() :v(0), left(NULL), right(NULL) {}
};

node *root;
node *newnode() { return new node(); }
void delnode(node *root)
{
	if (root == NULL)return;
	if (root->left)delnode(root->left);
	if (root->right)delnode(root->right);
	delete root;
}

void newtree()
{
	delnode(root);
	root=newnode();
}

bool read(int *a)
{
	n = 0;
	string line;
	if (!getline(cin, line))return 0;
	stringstream ss(line);
	int x;
	while (ss >> x)a[n++] = x;
	return n > 0;
}

node* build(int l1, int r1, int l2, int r2,node *u)
{
	if (l1 > r1)return 0;
	u->v = post_order[r2];
	int p = l1;
	while (in_order[p] != u->v)p++;
	int cnt = p - l1;

	u->left = newnode();
	u->left = build(l1, p - 1, l2, l2 + cnt - 1, u->left);
	u->right = newnode();
	u->right = build(p + 1, r1, l2 + cnt, r2 - 1, u->right);
	return u;
}

void dfs(node *root, int sum)
{
	sum += root->v;
	if (!root->left && !root->right)
	{
		if (sum < best_sum || (sum == best_sum && root->v < best))
		{
			best_sum = sum;
			best = root->v;
		}
	}
	if (root->left)dfs(root->left, sum);
	if (root->right)dfs(root->right, sum);
}

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	while (read(in_order) && read(post_order))
	{
		newtree();
		root=build(0, n - 1, 0, n - 1,root);
		best_sum = 100000000;
		dfs(root, 0);
		cout << best << endl;
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

比想象中稍稍麻烦一丢丢 但是真的只是一丢丢

我的代码真好看(可能只有我这么觉得……唔 各种意义上也是照着书上的来的 不是我的代码唔……算了……)

因为指针赋值的关系……关系build函数我把指针也加进去了 这样反而更清楚 不改build函数的参数列表 我还真的不想再想了 感觉以我的水平很容易把他的指向给弄乱 毕竟指了l还要指r……不对 其实我把指针加进build函数的初衷是因为build函数有个u 这个u不好new出来 然后后面u->left那边比较好new干脆就加到参数列表里了

这一遍感觉还行 改成指针也没有改多长时间

 

最后再独立打一遍就算过了……

 

等一下 我在打最后一遍的时候突然发现build可以有更简单的理解 我上边的理解很有可能是错的 对 第二个点那边 就是cnt是否“-1”那边


l[root] = build(l1, p - 1, l2, l2 + cnt - 1);
r[root] = build(p + 1, r1, l2 + cnt, r2 - 1);

之前也有提到l1-r1是in的的区间 而l2-r2是post的 然后 这两个区间表达的是同样的几个数字

我们看build函数的左半部分 也就是 l1,p-1,p+1,r2 加上 之前表达root的p 这就是整个in_order数组

右半部分 l2,l2,cnt-1 , l2+cnt,r2-1 还有表达root的n-1 这就是整个post_order数组

我的天 理了两天突然意识到了 这样理解不是很轻松了吗?

 

 

UVa 839 天平

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 10000 + 10;

bool solve(int &w)
{
	bool b1 = 1, b2 = 1;
	int w1, d1, w2, d2;
	cin >> w1 >> d1 >> w2 >> d2;
	if (!w1)b1 = solve(w1);
	if (!w2)b2 = solve(w2);
	w = w1 + w2;
	return b1 && b2 && (w1*d1 == w2 * d2);
}

int main()
{
	int t,w;
	cin >> t;
	while (t--)
	{
		if (solve(w))
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
		if (t)
			cout << endl;
	}
	//system("pause");
	return 0;
}

题意画一画很好理解 代码细细看一看很好理解

但是只有题意我是写不出代码的orz

基本原理是DFS ……我觉得是

往深了放d1,d2 然后 返回是否平衡

然后这道题 也因为引用而变得异常的精简

之前就有在想 如果不用引用会怎样 我想用全局变量代替引用

事实上我写了出来 而且 题目案例也过了 甚至Udebug的案例也都过了 然而……WA

这个也没法对拍啊我觉得 惆怅……

ps:这是我第二遍打这段文字 之前那段更随性更潇洒的因为没有保存消失了QAQ

 

AC了……惆怅的事情又来了

 

WA的代码

#include 
using namespace std;

int solve()
{
	int w1, d1, w2, d2;
	cin >> w1 >> d1 >> w2 >> d2;
//	bool b1 = true, b2 = true;
	if (!w1)w1=solve();
	if (!w2)w2=solve();
	if (w1*d1 == w2 * d2)
		return w1 + w2;
	return 0;
}

int main()
{
//	freopen("D:\\in.txt", "r", stdin);
//	freopen("D:\\out.txt", "w", stdout);
	int t;
	cin >> t;
	while (t--)
	{
		if (solve())
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
		if (t)
			cout << endl;
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

AC的代码

#include 
using namespace std;

bool f;

int solve()
{
	int w1, d1, w2, d2;
	cin >> w1 >> d1 >> w2 >> d2;
//	bool b1 = true, b2 = true;
	if (!w1)w1=solve();
	if (!w2)w2=solve();
	if (w1*d1 == w2 * d2)
		return w1 + w2;
	f = 0;
	return 0;
}

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int t;
	cin >> t;
	while (t--)
	{
		f = 1; 
		solve();
		if (f)
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
		if (t)
			cout << endl;
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

同志们 看到区别了吗 就因为多了一个f变量……这可以说明什么 说明solve的范围值w1+w2有可能是0

对 我想了很久只想到了这样一种可能

网上大量的博客放上的代码全部和紫书一模一样……于是 我翻来翻去 终于找到一篇 不是按紫书上的方法来的博客(https://www.xuebuyuan.com/zh-hant/3210973.html)感谢大佬

对……我没能自己找到错误 我也想到了可能返回值是0的可能 但是没法想怎么改 也没有想法 直到看到这篇博客的 幡然醒悟

好我冷静了……事实上……udebug里也有d=0的情况 但是 我WA的代码一样过了 所以现在我又开始怀疑自己的假设 所以……

solve的返回值可能是0 这个假设 到底是不是真正WA的原因 我决定鸽置!……XD

 

UVa 699 下落的树叶

我感觉我有感觉

在看代码详解之前我先把我的想法写下来 看我想的对不对

题目要求的是每一列的树的节点和 而每个节点的左儿子比节点本身的位置 我叫他横坐标 小1 而右儿子比节点本身大1 然后我们可以把根的位置设为0 然后左树的坐标都是负数 右树的坐标都是正数 我们可以定义一个结构体 设一个int保存他的位置 然后再用一个数组 下标是位置 这样就可以保存下所有的数 接下来就是建树和遍历

我觉得我的方法应该可以 就算书上的不是这样的方法我也要之后自己写一遍 【自信脸】

看书……

哦对……下标是负数了……

书上的和我想的差不多 

当然 简便了很多很多

还是太菜……虽然很想估计自己能想到很棒了这样的东西 但是……太菜了……

……其实可以记录最小的下标然后在计算每一列的值的时候加上这个最小的下标的绝对值 然后最小的下标就是0……???可以吗???

 

先搞书上的方法

#include 
#include 
using namespace std;

const int maxn = 200;
int sum[maxn];

void build(int p)
{
	int v;
	cin >> v;
	if (v == -1)return;
	sum[p] += v;
	build(p - 1);
	build(p + 1);
}

bool init()
{
	int v;
	cin >> v;
	if (v == -1)return false;
	memset(sum, 0, sizeof(sum));//这算一个小细节 如果先memset...再输入v判断v是否等于-1的话 memset可能白执行……(后加:其实多这一次也不会怎样我想多了)

	int p = maxn / 2;
	sum[p] += v;
	build(p - 1);
	build(p + 1);
	return true;//差点被这个导致的runtime error折腾死 warning还是要看的
}

int main()
{
	int cas = 0;
	while (init())
	{
		int p = 0;
		while (sum[p] == 0)p++;
		printf("Case %d:\n", ++cas);
		cout << sum[p++];
		while (sum[p] != 0)cout << " " << sum[p++];
		cout << endl << endl;
	}
	return 0;
}

如上述注释……被runtime error搞的怀疑人生 刚开始开了10000 然后觉得不会那么大去代码仓库看看刘老师写的200 再看看自己的10000吓到不敢说话……

完了之后就是error……贴源代码error……网上copy 要不就是建树要不error 即使换回我这个吓死人的10000也error……

其实横向200的树就很大很吓人了 1w……呵呵呵呵

后来的一次WA是因为Case那边的输出格式问题 空格我超注意的 万万没想到这里出了差错 被自己蠢哭

22日 帮室友家的麻辣烫发传单去了 用赚来的钱给老妈买了条丝巾当生日礼物 但是这天全程没有碰代码……(9.23)

其实在23号更新前 尝试着用指针打过一次 但是很明显 我失败了 - -

23号的计划是 先用指针键树然后dfs完成699 然后看书看懂下一题的代码 然后玩(玩心很重……)

#include 
#include 
using namespace std;

int sum[200];
bool f = 0;

struct node
{
	int v;
	int p;
	node *left;
	node *right;
	node() :v(0), p(0), left(NULL), right(NULL) {}
};

node *root;

node *newnode() { return new node(); }
void delnode(node *u)
{
	if (u == NULL) return;
	if (u->left)delnode(u->left);
	if (u->right)delnode(u->right);
	delete u;
}


int mi, ma;
node* build(node *u,int p)
{
	int v;
	cin >> v;
	if (v == -1) return NULL;
	u = newnode();
	u->v = v;
	u->p = p;
	if (mi > p)mi = p;
	if (ma < p)ma = p;
	u->left=build(u->left, p-1);
	u->right=build(u->right, p+1);
	return u;
}

bool read()
{
	int t;
	cin >> t;
	if (t == -1)return false;

	f = 0;
	memset(sum, 0, sizeof(sum));
	mi = 0, ma = -200;
	delnode(root);
	root = newnode();
	root->v = t;
	root->p = 0;
	root->left=build(root, 0-1);
	root->right=build(root, 0+1);
	if (root->left == NULL && root->right == NULL)
		f = 1;
	return true;
}

void dfs(node *u)
{
	if (u == NULL)return;
	//cout << u->v << " " << u->p<v;
	else
		sum[u->p + mi] += u->v;
	dfs(u->left);
	dfs(u->right);
}

int main()
{
	//freopen("d:\\in.txt", "r", stdin);
	//freopen("d:\\out.txt", "w", stdout);
	int cas = 0;
	while (read())
	{
		int ff = 0;
		if (mi < 0 && ma>=0)
		{
			ma = ma - mi + 1;
			mi = -mi;
		}
		else if (mi < 0 && ma < 0)
		{
			ma = ma - mi + 1;
			mi = -mi;
			ff = 1;
		}
		dfs(root);
		printf("Case %d:\n", ++cas);
		if (ma == -200)
			cout << sum[0] << endl<

基本思路就是 建树 然后dfs 再加起来 sum[p+mi] 这样的形式 但是!但是!因为我要执意0到ma输出 所以输出部分就开始变得恶心起来 出现了各种各样的【意料之外】mi 和 ma 有全为正数(即没有左树)全为负数(即没有右树)只有root(没有左右树)mi为负数 ma为正数 然后就……成上面那样了 别看……太丑 丑到我不想看第二遍……

 

#include 
#include 
using namespace std;

int sum[200];
bool f = 0;

struct node
{
	int v;
	int p;
	node *left;
	node *right;
	node() :v(0), p(0), left(NULL), right(NULL) {}
};

node *root;

node *newnode() { return new node(); }
void delnode(node *u)
{
	if (u == NULL) return;
	if (u->left)delnode(u->left);
	if (u->right)delnode(u->right);
	delete u;
}


int mi, ma;
node* build(node *u,int p)
{
	int v;
	cin >> v;
	if (v == -1) return NULL;
	u = newnode();
	u->v = v;
	u->p = p;
	if (mi > p)mi = p;
	if (ma < p)ma = p;
	u->left=build(u->left, p-1);
	u->right=build(u->right, p+1);
	return u;
}

bool read()
{
	int t;
	cin >> t;
	if (t == -1)return false;

	f = 0;
	memset(sum, 0, sizeof(sum));
	mi = 0, ma = -200;
	delnode(root);
	root = newnode();
	root->v = t;
	root->p = 0;
	root->left=build(root, 0-1);
	root->right=build(root, 0+1);
	if (root->left == NULL && root->right == NULL)
		f = 1;
	return true;
}

void dfs(node *u)
{
	if (u == NULL)return;
	//cout << u->v << " " << u->p<v;
	else
		sum[u->p + mi] += u->v;
	dfs(u->left);
	dfs(u->right);
}

int main()
{
	//freopen("d:\\in.txt", "r", stdin);
	//freopen("d:\\out.txt", "w", stdout);
	int cas = 0;
	while (read())
	{
		if (ma < 0)
			ma = 0;
		mi = -mi;
		ma = ma + mi;
		dfs(root);
		printf("Case %d:\n", ++cas);
		if (ma == -200)
			cout << sum[0] << endl<

然后 我把输出部分稍稍改变了一点 算是好了不少 第一次的是想算出mi到ma这个区间的数字有几个 但是我们其实可以直接把坐标移动一下 所以 mi+|mi| 然后ma+|mi| 就可以直接算出有几个数字 不用再纠结加减 还有一个就是ma也就是坐标最大值 的比较 0是不会被比较的 所以……如果mi ma都是负数的话会少一个 所以直接在输出部分对比了 所以我在想……ma的初值其实也可以和mi改成0……还可以再改进的其实……

 针对输出部分我觉得这种mi到ma的方法可能会比书本上while找到不是0的办法稍稍好一丢……?会……吗?

UVa 297 四分树

是的 因为我不及时的关闭 我又得重写了

首先 是书本上的问题

为什么四分树只需要先序遍历就可以确定整棵四分树

我觉得答案应该和完美二叉树(除了叶子 每个节点都有两个子节点 且叶子都在最底层 形成一个三角形)一样……?诶……一样吗?……????

应该一样…… 完美二叉树应该也可以用一个先序遍历确定 因为完全二叉树必须得都有左右两个子节点

为了确定 我的假设 百度了一下 发现完全二叉树就可以只用一个先序遍历确定 那我觉得完美二叉树应该也可以

好 以上的东西和我的问题其实一点都不搭嘎……

那么 以上两个二叉树以及四分树都有这样一个特点 一个节点下的子节点 一定得填满 二叉树节点下一定有两个子节点 四分树的节点下一定有四个子节点 所以 我们可以通过递归直接确认 一棵树

感觉我理清楚了……

接着是书上分析的那个部分

我调试然后跟着在纸上画的过程 其实是有点问题的 所以画出来的是关于32x32的的正方形关于其主对角线对称的这样一个图形……事实上我这样画对答案一点影响也没有 因为我们需要的是一个“画出来”的【过程】

所以结果是一个怎样的图形根本不重要,重要的是画出来的过程中有多少f 以及这个f是多大的像素……

好的我把代码打出来了 但是WA了 为什么呢 因为两行定义位置互换了一下 但是为什么位置互换就会WA呢?为什么!!!

WA

const int len = 32;
const int maxn = 1024 + 10;
int buf[len][len];
char s[maxn];
int cnt;

AC

const int len = 32;
const int maxn = 1024 + 10;
char s[maxn];
int buf[len][len];
int cnt;

目前最可靠的说法是 如果buf数组放不下了 就让串到s里 然后影响整个代码……

绝望……

 

开始我还以为是我的函数错了 但是……WA的问题在上面……

为了调试方便 我把两个for独立成一个函数了 这样调试会舒服些……

#include 
#include 
using namespace std;

const int len = 32;
const int maxn = 1024 + 10;
char s[maxn];
int buf[len][len];
int cnt;

void f(int r, int rw, int c, int cw)
{
	for (int i = r; i < rw; i++)
	{
		for (int j = c; j < cw; j++)
		{
			if (buf[i][j] == 0) { 
				buf[i][j] = 1; 
				cnt++; 
			}
		}
	}
}

void draw(const char *s, int &p, int r, int c, int w)
{
	char ch = s[p++];
	if (ch == 'p')
	{
		draw(s, p, r, c + w / 2, w / 2);
		draw(s, p, r, c, w / 2);
		draw(s, p, r + w / 2, c, w / 2);
		draw(s, p, r + w / 2, c + w / 2, w / 2);
	}
	else if (ch == 'f')
	{
		f(r, r + w, c, c + w);
	}
}

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int t;
	cin >> t;
	while (t--)
	{
		memset(buf, 0, sizeof(buf));
		cnt = 0;
		for (int i = 0; i < 2; i++)
		{
			cin >> s;
			int p = 0;
			draw(s, p, 0, 0, len);
		}
		printf("There are %d black pixels.\n", cnt);
	}
	//fclose(stdin);
	//fclose(stdout);
	//system("pause");
	return 0;
}

最后在打一遍6.3就算过掉了 四分树我觉得用指针对我来说有点勉强 姑且先鸽一下 以后再做尝试

 

UVa 572 油田

四个月前A过 方法好像和书上还不一样

#include 
#include 
using namespace std;

const int maxn = 110;
const int fx[8][2] = { 0,1,0,-1,1,0,-1,0,1,1,1,-1,-1,1,-1,-1 };
char s[maxn][maxn];
int cnt;

int n, m;
void dfs(int x,int y)
{
	s[x][y] = '*';
	for (int i = 0; i < 8; i++)
	{
		int xx = x + fx[i][0];
		int yy = y + fx[i][1];
		if (0<=xx && xx> n >> m && n && m)
	{
		cnt = 0;
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				cin >> s[i][j];
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (s[i][j] == '@')
				{
					dfs(i, j);
					cnt++;
				}
			}
		}
		cout << cnt << endl;
	}
	//system("pause");
	return 0;
}

这应该是最基本的DFS了 做法花样可以有很多 做法可能书上会好一点 毕竟……上面这种改了s数组 但是 这边只用了一个数组对吧……

……书上的方法我也做过 看了一会发现莫名熟悉 然后看看AC的记录我写过……变量名不一样而已……然后我稍稍改了下 就当我敲过吧 这个真的不难……

#include 
#include 
using namespace std;

const int maxn = 110;
const int fx[8][2] = { 0,1,0,-1,1,0,-1,0,1,1,1,-1,-1,1,-1,-1 };
char s[maxn][maxn];
int f[maxn][maxn];

int n, m;
void dfs(int x,int y,int cnt)
{
	f[x][y] = cnt;
	for (int i = 0; i < 8; i++)
	{
		int xx = x + fx[i][0];
		int yy = y + fx[i][1];
		if (0 <= xx && xx < n && 0 <= yy && yy < m && s[xx][yy] == '@' && f[xx][yy]==0)
			dfs(xx, yy, cnt);
	}
}

int main()
{
	while (cin >> n >> m && n && m)
	{
		int cnt = 0;
		memset(f,0,sizeof(f));
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				cin >> s[i][j];
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (s[i][j] == '@' && f[i][j]==0)
				{
					dfs(i, j,++cnt);
				}
			}
		}
		cout << cnt << endl;
	}
	//system("pause");
	return 0;
}

UVa 1103 象形文字(26日下午补 鸽了…… 大概周五补)

这道题!我发誓我会写的 一定会写的 而且我要单独写一个博客 这个题我觉得挺有意思的 但是我!不!会!【理直气壮】但是明天我会花一整个下午去弄回它的!

参考资料已经找好了 就等静下心来整它了

https://www.cnblogs.com/acm1314/p/4534360.html

 

UVa  816 Abbott的复仇

标题是25号打的 然后这些东西是26号写的

25号照着书上敲了一点 但是……一点都不知道自己敲的是什么

26号拷贝了代码仓库的源码 一点点调试了一下 稍稍能懂了一些

1.BFS的过程:和普通的DFS一样 判断是否在迷宫中 判断点和点之间是否可以走等条件  不同的是 书本中开了一个四维数组 和两个三维数组 d保存最短路的大小 为什么是最短路开始理解了 大家都是一起出发最先到终点的自然最短 还有一个p是保存着上一个点的坐标和方向 以便输出(大概……)然后 BFS的枚举我觉得有点意思 枚举了直走左转和右转 判断是否有这样一个路标允许自己这么走 四维数组has_edge 保存的是 坐标r、c、dir、turn 其中r c是坐标 而dir和turn是目前的方向和想要转的方向 r c dir是结构体中的参数 也是每次的行走的属性?反正是那样的感觉= = 然后枚举turn的方向 判断是否可以走 如果可以就加入队列 保存d p数组 

2.多维数组 有时候这个数组的多维运用就可以很巧妙 像这个has_edge 如果不是看书我觉得我这辈子都意识不到数组还可以这么玩……

3.

Node walk(const Node& u, int turn) {
  int dir = u.dir;
  if(turn == 1) dir = (dir + 3) % 4; // 逆时针
  if(turn == 2) dir = (dir + 1) % 4; // 顺时针
  return Node(u.r + dr[dir], u.c + dc[dir], dir);
}

太强了……真的……

这个函数是用来确定转向的 之前也说过BFS枚举转向 其中 u是当前的位置和面向 turn是0、1、2的枚举 0方向不改变 1是左转 而左转是逆时针 2是右转 右转是顺时针 也就是上边的注释的意思 那么 +3和+1什么意思呢……假如我现在面向E我要左转我就要-1  我要右转我就要+1 假如面向N 要左转 N是0 我要-1就负数了 但是我得给他变成3也就是W 我得给他+4 很麻烦对不对 但是我们可以模 就上面两行……

太强了……真的……

 

!AC了……

大一大二心态变化其实蛮大的

大一是卧槽 WA了 天哪 TLE了 艾玛 怎么又错了

大二就很佛性 惊了 AC了居然(这样的感觉)

 

这题真的还好人家刘老师的代码摆在那里 不然就这个格式就可以坑死人……我再研究研究这个格式去

格式问题:每一个迷宫姑且认为有个名字(输入的第一个字符串) 然后迷宫的名字顶格 之后每一行的开头空一格 然后每个坐标节点前是有一个空格的 “空(X,Y)”这样的感觉 题目所要求的是 除了标题以外 空两格 然后每两个坐标节点中间空一格 可以变成这样讲真的非常厉害 还有一个问题就是末尾的换行 如果要输出的节点数正好是10的倍数 那么末尾就会有两个空行 所以 刚好是10的倍数的时候就不可以输出回车 就考虑的超级周到……

 

最后是一个print_ans函数的问题

    while(1)
	{
		v.push_back(u);
		if (d[u.r][u.c][u.dir] == 0)break;
		u = p[u.r][u.c][u.dir];
	} 

	v.push_back(node(r0, c0, dir));

1.r0,c0,dir 很容易忘记 其实也不容易 因为我们bfs就是从r1,c1开始的 那么问题又来了 为什么要从r1,c1开始 这个一会再看

2.先把u加入vector再判断是否到起点

关于第二点 我试过do while 但是不知道为什么 第一个点总是会把第二点覆盖掉……调试了一下没有覆盖掉 只是根本没有进循环而已……然后我在do while循环末尾加了一个v.push_back(u);然后 如果只有起点和终点的话 d[u.r][u.c][u.dir] != 0这个条件就永远成立 然后就会TLE 失败……

至于第一点 因为r0 c0是起点 而我们的需要直行一段才到一个需要判断的路口

 

贴一个AC的代码

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

struct node
{
	int r, c, dir;
	node(int r = 0, int c = 0, int dir = 0) :r(r), c(c), dir(dir) {}
};

int dir, r0, c0, r1, c1, r2, c2;

const int maxn = 10;
char dirs[] = "NESW";
char turns[] = "FLR";

int dir_id(char c) { return strchr(dirs, c) - dirs; }
int turns_id(char c) { return strchr(turns, c) - turns; }

int dr[] = { -1,0,1,0 };
int dc[] = { 0,1,0,-1 };

int has_edge[maxn][maxn][4][3];
int d[maxn][maxn][4];
node p[maxn][maxn][4];

bool read()
{
	string s;
	cin >> s;
	if (s == "END")return false;
	cout << s << endl;

	cin >> r0 >> c0 >> s >> r2 >> c2;

	dir = dir_id(s[0]);
	r1 = r0 + dr[dir];
	c1 = c0 + dc[dir];

	memset(has_edge, 0, sizeof(has_edge));

	while (1)
	{
		int r, c;
		cin >> r;
		if (r == 0)break;
		cin >> c;
		while (cin >> s && s[0] != '*')
		{
			for (int i = 1; i < s.length(); i++)
			{
				has_edge[r][c][dir_id(s[0])][turns_id(s[i])]=1;
			}
		}
	}
	return true;
}

node walk(node u, int i)
{
	int dir = u.dir;
	if (i == 1)dir = (3 + dir) % 4;
	if (i == 2)dir = (1 + dir) % 4;
	return node(u.r + dr[dir], u.c + dc[dir], dir);
}

bool inside(int r, int c)
{
	return 1 <= r && r <= 9 && 1 <= c && c <= 9;
}

void print_ans(node u)
{
	vectorv;
	while(1)
	{
		v.push_back(u);
		if (d[u.r][u.c][u.dir] == 0)break;
		u = p[u.r][u.c][u.dir];
	} 

	v.push_back(node(r0, c0, dir));

	int cnt = 0;
	reverse(v.begin(), v.end());
	for (int i = 0; i < v.size(); i++)
	{
		if (cnt % 10 == 0)cout << " ";
		printf(" (%d,%d)", v[i].r, v[i].c);
		if (++cnt % 10 == 0)cout << endl;
	}
	if (v.size() % 10 != 0)cout << endl;
}

void solve()
{
	queueq;
	node t = node(r1, c1, dir);
	memset(d, -1, sizeof(d));
	d[t.r][t.c][t.dir] = 0;
	q.push(t);
	while (!q.empty())
	{
		node u = q.front(); q.pop();
		if (u.r == r2 && u.c == c2) { print_ans(u); return; }
		for (int i = 0; i < 3; i++)
		{
			node v = walk(u, i);
			if (has_edge[u.r][u.c][u.dir][i] && d[v.r][v.c][v.dir] < 0 && inside(v.r, v.r))
			{
				q.push(v);
				p[v.r][v.c][v.dir] = u;
				d[v.r][v.c][v.dir] = d[u.r][u.c][u.dir] + 1;
			}
		}
	}
	printf("  No Solution Possible\n");
}

int main()
{
	//freopen("d:\\in.txt", "r", stdin);
	//freopen("d:\\out.txt", "w", stdout);
	while (read())
	{
		solve();
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

和书上基本一致 只有输出部分改的(让我觉得)好看了一丢……

 

UVa 10305 给任务排序

谴责刘老师的格式……不!友!好!

虽然最终AC了但是在不必要的地方纠结了很久(原因是我笨看不懂大佬的格式)

代码改天重温的时候再贴再分析

 

然后我很开心的觉得自己学会了拓扑排序 然后去杭电找题目了

拓扑排序 HDU 4857

我万万没有想到第一题就不容易 开心的打完代码 运行 艾玛 数组不允许开那么大 然后翻博客看寻常的拓扑排序 (原理其实之前也有找过)然后想到了最暴力的方法——判断入度 加上优先队列完美 WA……

#include 
#include 
#include 
#include 
#include 
#define maxn 30007
using namespace std;
struct cmp
{
	bool operator()(const int &a, const int &b)
	{//从大到小 
		return a > b;//注意在结构体中以第二元素为基准 
	}
};

int T;
int n, m;
int in[maxn];
int c[maxn];
vector edge[maxn];
priority_queue,cmp> q;

bool dfs(int u)
{
	c[u] = -1;
	for (int v = 0; v < edge[u].size(); v++)
	{
		if (c[v] < 0)return false;
		if (c[v] == 0)dfs(v);
	}
	return true;
}

bool cycle()
{
	memset(c, -1, sizeof(c));
	for (int i = 1; i <= n; i++)
	{
		if (edge[i].size())
		{
			if (c[i] == 0)
				if(dfs(i)==false)return false;
		}
	}
	return true;
}

int main()
{
	scanf("%d", &T);
	while (T--)
	{
		memset(in, 0, sizeof(in));
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
			edge[i].clear();
		int a, b;
		for (int j = 1; j <= m; j++) {
			scanf("%d%d", &a, &b);
			in[b]++;
			edge[a].push_back(b);
		}


		if (cycle()==false)
		{
			cout << "you huan er" << endl;
		}

		for (int i = 1; i <= n; i++)
			if (in[i]==0) q.push(i);//入度=0 入队

		vector ans;
		while (!q.empty()) {
			a = q.top();
			ans.push_back(a);
			q.pop();
			for (int j = 0; j < edge[a].size(); j++) {
				b = edge[a][j];
				in[b]--;
				if (in[b] == 0) q.push(b);
			}
		}
		for (int i = 0; i < ans.size(); i++)
			cout << ans[i] << " ";
		cout << endl;
	}
	system("pause");
	return 0;
}

交给室友纠结我做别的题去了……

啊对了 判断是否为环那里函数还是书上的方法 输出的结果纯属卖萌 题目说好了保证有结果

 

好的室友纠结出结果了

5 3
5 2
3 1
3 4
正确输出应该是:
3 1 5 2 4

——来自原题讨论区

 

我的程序(以及我)都一致认为 答案应该是3 1 4 5 2 但是!但是!2确实可以在4的前面 因为2和4算同一层优先的 所以……但是为什么一定得反向实现 还有……就很烦我觉得……

 

 

拓扑排序 HDU 1258

同一个世界 同一个题意 你看人家这题就很友好 裸的拓扑排序 然后轻轻松松AC多好 复习了算法增加了AC题(请停下)

正经的 这个题其实和上一题描述类似

原文:“符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。”

所以!我就不明白了为什么上边就这样纠结 是这道题数据水了吗?

#include 
#include 
#include 
#include 
#include 
#define maxn 30007
using namespace std;
struct cmp
{
	bool operator()(const int &a, const int &b)
	{//从大到小 
		return a > b;//注意在结构体中以第二元素为基准 
	}
};

int T;
int n, m;
int in[maxn];
int c[maxn];
vector edge[maxn];
priority_queue,cmp> q;

bool dfs(int u)
{
	c[u] = -1;
	for (int v = 0; v < edge[u].size(); v++)
	{
		if (c[v] < 0)return false;
		if (c[v] == 0)dfs(v);
	}
	return true;
}

bool cycle()
{
	memset(c, -1, sizeof(c));
	for (int i = 1; i <= n; i++)
	{
		if (edge[i].size())
		{
			if (c[i] == 0)
				if(dfs(i)==false)return false;
		}
	}
	return true;
}

int main()
{
	while (cin >> n >> m)
	{
		memset(in, 0, sizeof(in));
		for (int i = 1; i <= n; i++)
			edge[i].clear();
		int a, b;
		for (int j = 1; j <= m; j++) {
			scanf("%d%d", &a, &b);
			in[b]++;
			edge[a].push_back(b);
		}


		if (cycle()==false)
		{
			cout << "you huan er" << endl;
		}

		for (int i = 1; i <= n; i++)
			if (in[i]==0) q.push(i);//入度=0 入队

		vector ans;
		while (!q.empty()) {
			a = q.top();
			ans.push_back(a);
			q.pop();
			for (int j = 0; j < edge[a].size(); j++) {
				b = edge[a][j];
				in[b]--;
				if (in[b] == 0) q.push(b);
			}
		}
		for (int i = 0; i < ans.size()-1; i++)
			cout << ans[i]<<" ";
		cout <

 

拓扑排序 HDU 3342

题意是判断输入的数据是否有环 有环输出NO 否则输出YES

突然发现我判断环的方式可能有问题 上边的判环可能全部都错了= = 反正也没人看这博客本来就是写来给自己理思路的 不改应该没问题吧……改天改?

#include 
#include 
using namespace std;

const int maxn = 110;
int g[maxn][maxn];
int c[maxn];
int n, m;

bool dfs(int u)
{
    c[u] = -1;
    for (int v = 0; v < n; v++)
    {
        if (g[u][v])
        {
            if (c[v] < 0)return false;
            if (c[v] == 0)
                if (dfs(v) == false)return false;
        }
    }
    c[u] = 1;
    return true;
}

bool ss()
{
    memset(c, 0, sizeof(c));
    for (int u = 0; u < n; u++)
    {
        if (c[u] == 0)
            if (dfs(u) == false)return false;
    }
    return true;
}

int main()
{
    while (cin >> n >> m && n && m)
    {
        memset(g, 0, sizeof(g));
        for (int i = 0; i < m; i++)
        {
            int u, v;
            cin >> u >> v;
            g[u][v] = 1;
        }
        if (ss())
        {
            cout << "YES" << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }
    return 0;
}

直接用了书上的判环方式 因为n也不是很大嘛 就直接这样咯……

#include 
#include 
#include 
using namespace std;

const int maxn = 110;
//int g[maxn][maxn];
vectorg[maxn];
int c[maxn];
int n, m;

void init()
{
	for (int i = 0; i < maxn; i++)
		g[i].clear();
}

bool dfs(int u)
{
	c[u] = -1;
	for (int v = 0; v < g[u].size(); v++)
	{
		int vv = g[u][v];
		if (c[vv] < 0)return false;
		if (c[vv] == 0)
			if (dfs(vv) == false)return false;
	}
	c[u] = 1;
	return true;
}

bool ss()
{
	memset(c, 0, sizeof(c));
	for (int u = 0; u < n; u++)
	{
		if (c[u] == 0)
			if (dfs(u) == false)return false;
	}
	return true;
}

int main()
{
	while (cin >> n >> m && n && m)
	{
		init();
		for (int i = 0; i < m; i++)
		{
			int u, v;
			cin >> u >> v;
			g[u].push_back(v);
		}
		if (ss())
		{
			cout << "YES" << endl;
		}
		else
		{
			cout << "NO" << endl;
		}
	}
	return 0;
}

后续的练习有需要 改成vector又A了一发 爽到……

 

UVA - 10129 Play on Words

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 100005;


struct node
{
	int in, out;
};

int b[30];//并查集
node d[30];//度
int s[27][27];

int find(int x)
{
	if (x != b[x])
		b[x] = find(b[x]);
	return b[x];
}

void unio(int x, int y)
{
	int q = find(x);
	int p = find(y);
	if (q != p)
		b[q] = p;
}

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int n, m;
	int t;
	cin >> t;
	while (t--)
	//while(1)
	{
		memset(d, 0, sizeof(d));
		memset(b, -1, sizeof(b));
		char t[1005];
		cin >> n;
		int flag = 1;
		node qd, zd;
		if (n == 1)
		{
			scanf("%s", &t);
			cout << "Ordering is possible." << endl;
			continue;
		}
		for (int i = 0; i < n; i++)
		{
			scanf("%s", &t);
			int u = t[0] - 'a';
			int v = t[strlen(t) - 1] - 'a';
			if (b[u] == -1)
				b[u] = u;
			if (b[v] == -1)
				b[v] = v;
			d[u].in++;
			d[v].out++;
			unio(u, v);
		}
		int cnt = 0;
		for (int i = 0; i < 30; i++)
		{
			if (b[i] == i)
				cnt++;
		}
		if (cnt == 1)//只有一个并查集
		{
			int incnt = 0;
			for (int i = 0; i < 30; i++)
			{
				if (b[i] != -1 && d[i].in != d[i].out)
				{
					if (d[i].in - d[i].out == 1)
					{
						qd = d[i];
					}
					else if (d[i].out - d[i].in == 1)
					{
						zd = d[i];
					}
					else
					{
						flag = 0;
						break;
					}
				}
			}
		}
		else
			flag = 0;

		if (flag)
			cout << "Ordering is possible." << endl;
		else
			cout << "The door cannot be opened." << endl;
	}
	//fclose(stdin);
	//fclose(stdout);
	//system("pause");
	return 0;
}

优雅的并查集先来一发。

1.连通性 由于有些点没有出现过,但是初始化的时候会是自身所以干脆让并查集的数组先赋值为-1 输入时出现了点才是点等于自身,即进行初始化 ,通过遍历并查集的数组判断连通性,如果连通那么只会有一个元素等于下标。

2.欧拉路 或 欧拉路径 这题很明显是有向图——顺便提一下,我看书不仔细,把单词理解成了节点,所以我定义了一个结构体,即是一个单词,有进有出,仿佛是一个个房间,然后有只能进的门还有只能出的门。——总而言之,如果出度比入度大一 即为起点 出度比入度小一即为终点,其实我上边写的是不对的,我不该保存起点和终点的值,应该保留起点和重点的数量……当然其实也没用,因为并查集已经判过连通性……我的意思在于如果是欧拉路径,那么起点和终点只能分别有一个,且其他的点出度入度应该相等。

3.特殊情况

n==1 时 应该判断为可以欧拉路径

 

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 100005;

int l[30][30];
int vis[30][30];

void dfs(int in)
{
	for (int out = 0; out < 30; out++)
	{
		if (l[in][out] == 1 && vis[in][out] == 0)
		{
			vis[in][out] = 1;
			dfs(out);
		}
	}
}

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int T;
	cin >> T;
	while (T--)
	{
		int n;
		scanf("%d", &n);
		int in[30] = { 0 };
		int out[30] = { 0 };
		memset(l, 0, sizeof(l));
		memset(vis, 0, sizeof(vis));
		for (int i = 0; i < n; i++)
		{
			char s[1005];
			scanf("%s", &s);
			int u = s[0] - 'a';
			int v = s[strlen(s) - 1]-'a';
			l[u][v] = 1;
			in[u]++;
			out[v]++;
		}
		int first=0;
		for (int i = 0; i < 30; i++)
		{
			if (in[i] - out[i] == 1)
			{
				dfs(i);
				first = 1;
			}
		}
		if(!first)
			for (int i = 0; i < 30; i++)
			{
				if (in[i]!=0 && in[i] == out[i])
				{
					dfs(i);
					break;
				}
			}
			
		int flag = 1;
		for (int i = 0; i < 30 && flag; i++)
		{
			for (int j = 0; j < 30 && flag; j++)
			{
				if (l[i][j] == 1 && vis[i][j] == 0)
				{
					flag = 0;
					break;
				}
			}
		}
		int cnt1 = 0, cnt2 = 0;
		for (int i = 0; i < 30; i++)
		{
			if (in[i] != out[i])
			{
				if (in[i] - out[i] == 1)
					cnt1++;
				else if (out[i] - in[i] == 1)
					cnt2++;
				else
				{
					flag = 0;
					break;
				}
			}
		}
		if (cnt1 > 1 || cnt2 > 1)
			flag = 0;
		if (flag)
			cout << "Ordering is possible." << endl;
		else
			cout << "The door cannot be opened." << endl;
	}
	//fclose(stdin);
	//fclose(stdout);
	//system("pause");
	return 0;
}

dfs可以连通和欧拉回路/欧拉路径一起判断

1.遍历所有点 如果有一个点满足起点的条件,那么进行dfs递归遍历,访问到无法在访问,如果是欧拉路径那么就可以遍历所有点,将所有道路标记,反之则不连通。

2.如果是欧拉回路,那么不会有起点,找到一个任意的点,进行dfs。如果是欧拉回路,那么就可以遍历到所有的点,反之则不连通。

3.由于只需要满足起点的条件就可以进行dfs,所以n==1时会被视为一个起点和一个终点,无需再特别判断。

 

#include
#include
#include
using namespace std;

const int maxn = 1000 + 5;

// 并查集(代码摘自《算法竞赛入门经典——训练指南》第三章)
int pa[26];
int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; }

int used[26], deg[26]; // 是否出现过;度数

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		int n;
		char word[maxn];

		scanf("%d", &n);
		memset(used, 0, sizeof(used));
		memset(deg, 0, sizeof(deg));
		for (int ch = 0; ch <= 26; ch++) pa[ch] = ch; // 初始化并查集
		int cc = 26; // 连通块个数

		for (int i = 0; i < n; i++) {
			scanf("%s", word);
			char c1 = word[0]-'a', c2 = word[strlen(word) - 1]-'a';
			deg[c1]++;
			deg[c2]--;
			used[c1] = used[c2] = 1;
			int s1 = findset(c1), s2 = findset(c2);
			if (s1 != s2) { pa[s1] = s2; cc--; }
		}

		vector d;
		for (int ch = 0; ch <= 26; ch++) {
			if (!used[ch]) cc--; // 没出现过的字母
			else if (deg[ch] != 0) d.push_back(deg[ch]);
		}
		bool ok = false;
		if (cc == 1 && (d.empty() || (d.size() == 2 && (d[0] == 1 || d[0] == -1)))) ok = true;
		if (ok) printf("Ordering is possible.\n");
		else printf("The door cannot be opened.\n");
	}
	return 0;
}

可恶啊,这个姓刘的怎么这么聪明,我好崇拜他啊!!

数组刘老师开的比较大,不大好调试,我就擅自改成了27。

入度+1出度-1。

如果是欧拉回路那么全部等于0,即d.empty()==1的情况。如果元素=+1那么就是起点 如果是-1那么为终点。

cc这个变量更是妙的很,乍看之下没啥卵用,但事实上cc是在判断是否连通,没有出现的过的点需要减去,并查集合并的时候减去并掉的一个节点,妙啊妙啊……

d.size是判断有多少个点出度和入度是不一样的,入vector的条件那边就可以看出来。

 

UVA - 10562 Undraw the Trees 

#include 
#include 
#include 
using namespace std;

const int maxn = 200 + 5;
char t[maxn][maxn];
int n;

int read()
{
	int i=0;
	for (;; )
	{
		fgets(t[i], maxn, stdin);
		if (t[i][0] == '#')
			break;
		t[i][strlen(t[i]) - 1] = '\0';
		i++;
	}
	return i;
}

void dfs(int h,int l)
{
	printf("%c(", t[h][l]);
	if (h + 1= 0 && t[h + 2][i-1] == '-')i--;
		while (t[h + 2][i] == '-' && t[h + 3][i] != '\0')
		{
			if (t[h + 3][i] != '\0' && t[h + 3][i] != ' ')
			{
				dfs(h + 3, i);
			}
			i++;
		}
	}
	printf(")");
}

int main() 
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int T;
	scanf("%d", &T);
	getchar();
	while (T--)
	{
		memset(t, 0, sizeof(t));
		n=read();
		//for (int i = 0; i < n; i++)cout << t[i]<

题意和分析紫书都有,我也不加赘述,省的再乱。二维数组递归实现嘛……

讲一下我的心路历程:初看题目,傻逼了,再看分析,明白了,上手打代码,又发现这里不对那里不行,对着书本这里改改那里改改,现在倒是基本一致了。

难度不是很大,主要是细节,手里的书不是自己的,不然得做上笔记:空树 结点符号 任意可打印字符

值得一提的是 udebug里有一组数据我认为答案是错的,可能又是我自作聪明,先放下面交给大家判断了。我当咸鱼……

1
  A
 #|#
 ---
 #B#
#
udebug (A(B()))
my     (A(#()B()#()))
//既然是任意可打印字符那么#也是可以打印的 那么#也应该视作节点
//我的代码会这样判断 我和书本作者类似 那么书本答案应该也是我的这个答案

 

UVa12171 雕塑 三维BFS+离散化(跳了)

 

UVa1572 自组合

#include 
#include 
#include 
using namespace std;

const int maxn = 55;
char G[maxn][maxn];
int c[maxn];

int n;

int ID(int a, int b)
{
	return (a - 'A') * 2 + (b == '+' ? 0 : 1);// 编号的顺序是从0开始 0 A+ 1 A- 2 B+ 3 B- 以此类推
											  // 我也有设想过 A+ B+ C+这样的拍法 但是 下边要把点统一就会很麻烦 
}

void connect(char a1, char a2, char b1, char b2)
{
	if (a1 == '0' || b1 == '0')return;
	int u = ID(a1, a2) ^ 1;//这题的最大难点是要把题目抽象成这个拓扑排序 而这个代码的难点 我觉得是这个 这个异或十分巧妙 将2n<=>2n+1 互相转换了 位运算实在强大
	int v = ID(b1, b2);	   //(接上)又因为^方便的缘故再让我想这个A+B+的排法我是觉得怎么都不如一个异或来的漂亮
	G[u][v] = 1;
}

bool dfs(int u)
{
	c[u] = -1;
	for (int i = 0; i < 52; i++)
	{
		if (G[u][i])
		{
			if (c[i] < 0)return true;
			else if (c[i] == 0 && dfs(i))return true;
		}
	}
	c[u]=1;
	return false;
}

bool judge()
{
	memset(c, 0, sizeof(c));
	for (int i = 0; i < 55; i++)
	{
		if(!c[i])
			if (dfs(i))return true;
	}
	return false;
}

int main()
{
	//freopen("D:\\in.txt", "r", stdin);
	//freopen("D:\\out.txt", "w", stdout);
	int n;
	while (cin >> n)
	{
		memset(G, 0, sizeof(G));
		while (n--)
		{
			string s;
			cin >> s;
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					if (i != j)
					{
						connect(s[i * 2], s[i * 2 + 1], s[j * 2], s[j * 2 + 1]);
					}
				}
			}
		}
		if (judge())cout << "unbounded" << endl;
		else cout << "bounded" << endl;
	}
	//fclose(stdin);
	//fclose(stdout);
	//system("pause");
	return 0;
}

异或运算的非常漂亮,需要的几个点已经注释在上边了。

 

UVa 1599 理想路径

vj炸了 - - 打算一段一段看代码 可能会容易看懂一点 整段调试也难受

//定义部分
const int maxn = 100000 + 5;
const int INF = 1000000000; // maximal color

struct Edge {
  int u, v, c;
  Edge(int u=0, int v=0, int c=0):u(u),v(v),c(c) {}
};

vector edges;
vector G[maxn];

maxn是最大的点的数目,inf是为求最小步数的值而定义的最大值。

edge结构体——u、v分别为起点和终点,c是该道路的颜色。

vector edge 保存所有的道路

G[maxn] vector数组 保存所有……应该也是道路我觉得 一会在上来改把

//主函数
int main() {
  int u, v, c, m;
  while(scanf("%d%d", &n, &m) == 2) {
    edges.size();
    for(int i = 0; i < n; i++) G[i].clear();
    while(m--) {
      scanf("%d%d%d", &u, &v, &c);
      AddEdge(u-1, v-1, c);
      AddEdge(v-1, u-1, c);
    }
    rev_bfs();
    bfs();
  }
  return 0;
}

有n个点m条边。

edge.clear()清空…… 然后for循环 清空G数组

然后输入m条边,u和v各-1应该是为了减少空间的利用,addedge应该是将u、v、c加入到edge中,由于是无向图,所以加两次。

剩下两个bfs函数一会再看

void AddEdge(int u, int v, int c) {
  edges.push_back(Edge(u, v, c));
  int idx = edges.size() - 1;
  G[u].push_back(idx);
}

将节点加入到edge中很好看懂但是之后的操作……

idx是为了编号 第二次更新 是为了给路径编号 因为vector是从0开始顺序储存 所以可以用idx直接调用

G数组的用途越来越迷——啊我懂了 G的意思是有一条从u开始的路,然后终点是v 颜色是c,然后终点(并不是 是路)的编号是idx

//bfs1
int n, vis[maxn];
int d[maxn];
// reverse bfs to find out the distance from each node to n-1
void rev_bfs() {
  memset(vis, 0, sizeof(vis));
  d[n-1] = 0;
  vis[n-1] = true;

  queue q;
  q.push(n-1);
  while(!q.empty()) {
    int v = q.front(); q.pop();
    for(int i = 0; i < G[v].size(); i++) {
      int e = G[v][i];
      int u = edges[e].v;
      if(!vis[u]) {
        vis[u] = true;
        d[u] = d[v] + 1;
        q.push(u);
      }
    }
  }
}

因为我们输入的时候u和v都减了1 所以现在的n也要-1 (所以干嘛之前也去-1了 我觉得不缺这点啊= = 第二次更新:还真的不是缺这么一点,因为我们的vector是从0开始的 如果从1开始编路径 特别容易乱)

d数组可以看出来是保存距离的 vis是保存是否访问过的 我们这个bfs的目的是得到每个点到终点的距离

所以从终点bfs出来到1(那么 为什么不起点bfs出去 每步-1……第三遍:减什么减 你知道最短路是多少了吗? 新问题又来了 为什么不能从起点开始到终点算d[n]保存最短的路径是多少)

v从队列中取出 然后遍历以v为起点的道路 G[v][i]的意思是以v为起点的第i条路要通往的e点 e点的值是之前addedge函数的idx值

然后知道了idx的值 就可以直接调用edge的内容 u是道路的终点

然后呈圈向外扫 边扫边+1 作为距离终点的步数。

vector ans;

// forward bfs to construct the path
void bfs() {
  memset(vis, 0, sizeof(vis));
  vis[0] = true;
  ans.clear();

  vector next;
  next.push_back(0);
  for(int i = 0; i < d[0]; i++) {
    int min_color = INF;
    for(int j = 0; j < next.size(); j++) {
      int u = next[j];
      for(int k = 0; k < G[u].size(); k++) {
        int e = G[u][k];
        int v = edges[e].v;
        if(d[u] == d[v] + 1)
          min_color = min(min_color, edges[e].c);
      }
    }
    ans.push_back(min_color);

    // find out the next vertices of the next phase
    vector next2; 
    for(int j = 0; j < next.size(); j++) {
      int u = next[j];
      for(int k = 0; k < G[u].size(); k++) {
        int e = G[u][k];
        int v = edges[e].v;
        if(d[u] == d[v] + 1 && !vis[v] && edges[e].c == min_color) {
          vis[v] = true;
          next2.push_back(v);
        }
      }
    }
    next = next2;
  }

  printf("%d\n", ans.size());
  printf("%d", ans[0]);
  for(int i = 1; i < ans.size(); i++) printf(" %d", ans[i]);
  printf("\n");
}

这段就有点长了,第一遍循环(j)找点u为起点的路径,其中颜色最小的是哪一个,第二遍循坏(j),找到下一个点,然后再赋值回next。

因为比较晚了所以研究的不细。

但是还是有几个点是已经有结论的。

首先是第一遍bfs已经找出了最短的路和最短路径的走法,其实看到这里我已经有想法,可以用string直接把颜色保存下来,然后循环遍历找出最小的string便是答案,但是……会超时的?姑且先不论。

因为有了最短的走法,所以1,2比1,2,3短这种事情就不会发生,因为1,2这样的走法已经是最短的,第一个循环(i)也已经把最短的路径固定死了。所有这样的走法一定是最短路。

不得不感叹其高明!服气!

#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 100000 + 5;
const int inf = 1000000000;

int n, m;
struct node
{
	int u, v, c;
	node(int u,int v,int c):u(u),v(v),c(c){}//应该是C++的继承 为了使用edge.push_back(node(u,v,c))这样的形式
};

vectoredge;
vectorG[maxn];
void addnode(int u, int v, int c)
{
	edge.push_back(node(u, v, c));
	int idx = edge.size() - 1;
	G[u].push_back(idx);
}

int vis[maxn];
int d[maxn];
void rev_bfs()
{
	memset(vis, 0, sizeof(vis));
	int t = n - 1;
	vis[t] = 1;
	d[t] = 0;

	queueq;
	q.push(t);
	while (q.size())
	{
		int v = q.front(); q.pop();
		for (int i = 0; i < G[v].size(); i++)
		{
			int e = G[v][i];
			int u = edge[e].v;
			if (vis[u] == 0)
			{
				vis[u] = 1;
				d[u] = d[v] + 1;
				q.push(u);
			}
		}
	}
}

vectorans;
void bfs()
{
	memset(vis, 0, sizeof(vis));
	ans.clear();
	vis[0] = 1;

	vectornext;
	next.push_back(0);
	for (int i = 0; i < d[0]; i++)
	{
		int minn = inf;
		for (int j = 0; j < next.size(); j++)
		{
			int u = next[j];
			for (int k = 0; k < G[u].size(); k++)
			{
				int e = G[u][k];
				int v = edge[e].v;
				if (d[u] == d[v] + 1)
					minn = min(minn, edge[e].c);
			}
		}
		ans.push_back(minn);

		vectornext2;
		for (int j = 0; j < next.size(); j++)
		{
			int u = next[j];
			for (int k = 0; k < G[u].size(); k++)
			{
				int e = G[u][k];
				int v = edge[e].v;
				if (d[u] == d[v] + 1 && edge[e].c==minn && vis[v]==0)
				{
					vis[v] = 1;
					next2.push_back(v);
				}
			}
		}
		next = next2;
	}
	cout << ans.size() << endl;
	cout << ans[0];
	for (int i = 1; i < ans.size(); i++)
		cout << " " << ans[i];
	cout << endl;
}

int main()
{
	while (cin >> n >> m)
	{
		memset(G, 0, sizeof(G));
		edge.clear();
		for (int i = 0; i < m; i++)
		{
			int u, v, c;
			cin >> u >> v >> c;
			addnode(u - 1, v - 1, c);
			addnode(v - 1, u - 1, c);
		}
		rev_bfs();
		bfs();
	}
	return 0;
}

本来还想再写一遍注释理一理思路的 但是好像可以分析的这边也已经敲过一遍了 倒是写的过程中冒出了不少新的想法想试试

刚刚注意到了一个重要的地方——memset可以用来给vector赋值 但是 这样做并不好 十分的危险 

其实memset清0操作对于结构体或者类都必须慎重,因为很容易破坏自身的数据结构,最典型的就是带有虚函数的类,一旦清0连虚表都给破坏掉了。——引用自大佬博客 侵删

 

计划

加油中……

接下来的计划:17日满课 晚上练习指针链表(成功?了一半?有一半?) 用指针链表做题(失败) 16日-23日 阅读完6.3 平均一天一题

19日 一个下午窝实验室(结果大多时间在摸鱼)截止15:36 uva122 能用指针敲完 尝试全用数组做一遍 uva548可以看懂代码 

接下来的计划是先把uva 122再独立敲一遍 然后改成数组做一遍 再试着敲uva548

最后是6.4 打算用3到4天看完 然后接下来的一个星期看完6.5 最后的6.6的练习……尽力而为吧 尽可能做到书本要求的8道……(失败)

10.19 万万没想到才看完6.4 ←←而且除了6.4还有6.5 傻了吧……

10.20 被作业弄的火气很大,完了又被练习赛(事实上是CCPC的题目)还有CF的题目弄的没脾气,也不想补题了,心累,完了以后注意了一直没法集中,在实验室呆了三个小时几乎什么都没干,打的代码估计连50行都没有……这个人算是真的没用了……6.5的带星号的题估计都不会去写 感觉很难的样子 不为难自己了 这样一来就只剩三道了

最近其实一直在学东西,这个啊那个啊,虽然题目做的很少,但可能真的是压力太大了反而效率降低了,打算明天放个假,也争取把UVa1572写出来其实看懂就可以了……明天再说把 今天是真的累了

10.21 摸了一个下午 算是舒舒服服 顺带边玩边看算是把UVa1572 看明白了 明天满课 晚上社团 早点来实验室然后看第二题 课后练习打算重新开个博客 这个写的太满了……

 

其他/抱怨:

 一个暑假集训四个星期,收获有,但是很少,大概把所讲的算法了解了一下,但是也只是停留在了解这个层面。

基本就是——忘了。

就算暂时记住 但是一旦真正要用的时候就会出现这样那样的问题。BFS DFS都这样何况其他。

反正……目前的状态就是感觉压力很大,各种意义上的压力 然后最近又开始焦虑,感觉一开始打代码身上就开始出虚汗= =|||

睡眠质量也不怎样。

一个是感觉周围的人和自己的水平越来越远。另一个在害怕淘汰赛。

很好解决多打代码嘛……但是就回到了焦虑这个问题上。因为焦虑打代码因为打代码焦虑 然后恶性循环。orz

还有暑假集训的时候还有很多题没有补 就很烦躁……

CSDN更新了 目录简直NICE

你可能感兴趣的:(UVA)