算法竞赛刷题笔记ix

第二章 练习 Part II

POJ 1635 Subway tree system

在这里插入图片描述
这个题有点意思,因为每条边只走两次,所以当出现1时出说明有一个分支已经走完了 比如01 说明树有一个只有一个结点的子树。
我的一个想法就是比较两个字符串中含有连续0的种类和个数是否一样(因为输入是correct exploration)写出了代码 运行起来发现示例没过,仔细观察了一下 发现自己太NAIVE了
示例的前两个 第二有连续的3个零 而第一没有 但是他们是same的
修改一下 每回碰到1的时候不重新计数,而是把cnt减一 这样cnt就表示层数了,改完以后的代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<utility>
#include<climits>
#include<cstring>
#include<cassert>
using namespace std;
const int N = 3004;

char s[N],t[N];
int cnts[N],cntt[N];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        memset(cnts,0,sizeof(cnts));
        memset(cntt,0,sizeof(cntt));
        scanf("%s",s+1);
        scanf("%s",t+1);
        int cnt = 0,tots = 0,tott = 0,flag = 1,flip = 0;//flag为0时说明发现了不等 那么结果就为false
        for(int k=1;s[k];k++){
            if(s[k] == '0')
                cnt++,tots++,flip = 1;
            else{
                cnts[cnt]++;
                cnt--;
                flip = 0;//flip是控制当连续个1出现时 第二次出现1开始不更新cnt直到0出现为止
            }
        }
        cnt = 0;
        for(int k=1;t[k];k++){
            if(t[k] == '0')
                cnt++,tott++,flip = 1;
            else{
                cntt[cnt]++;
                cnt--;
                flip = 0;
            }
        }
        for(int w=1;tots && tott;w++){
            if(cnts[w] == cntt[w])
                tots -=cnts [w],tott -= cntt[w];
            else{
                flag = 0;
                break;
            }
        }
        if(flag && !tots && !tott) puts("same");
        else puts("different");
    }
    return 0;
}

测试数据就一个 好难debug啊。。
我想到比较两个树的所有叶子结点的深度是否相等来判断 后来发现反例

0 - 1 0 - 2 1 - 3 1 - 4 1 - 5 2 - 6
0 - 1 0 - 2 1 - 3 1 - 4 2 - 5 2 - 6
两者都是7个结点 且所有叶子结点 4个深度为2(层数为3)

光盘里面的方法是 最小表示法 康一康:

#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
string s1, s2;

string w(string s) {
	vector<string> a;
	string f = "";
	int num = 0, t = 0;
	for (unsigned int i = 0; i < s.size(); i++) {
		if (s[i] == '0') num++;
		else num--;
		if (!num) {
			if (i - 1 > t + 1) a.push_back("0" + w(s.substr(t + 1, i - 1 - t)) + "1");
			else a.push_back("01");
			t = i + 1;
		}
	}
	sort(a.begin(), a.end());
	for (unsigned int i = 0; i < a.size(); i++) f += a[i];
	return f;
}

int main() {
	int t;
	cin >> t;
	while (t--) {
		cin >> s1 >> s2;
		cout << (w(s1) == w(s2) ? "same" : "different") << endl;
	}
	return 0;
}

w就是把一棵树变成最小表示,w函数里面,遍历所有字符,通过num来找到当前结点的一棵子树,num为0时 s [ t , i ] s[t,i] s[t,i]就是一棵子树 然后递归地处理 s [ t , i ] s[t,i] s[t,i] ,得到 s [ t , i ] s[t,i] s[t,i]的最小表示,遍历完所有字符也就得到了所有子树的最小表示,然后进行排序之后 拼接起来 就是以当前结点为根的数的最小表示!

POJ 2185 Milking Grid

在这里插入图片描述
这题看的头疼,
他要的rectangular unit of smallest area,看这个输出感觉他要的是一维的rectangular unit
如果碰到

ABC
DEF

的话,仅仅是一维是不够的 又看到一个条件:dimensions of the small rectangular unit do not necessarily need to divide evenly the dimensions of the entire milking grid,说明ABCDEF就能满足要求
我理解错了,题目要求的输出是area of the smallest unit,输出的是面积 所以不用考虑一维不一维的
这题看了yxc的解析 没看懂 有点郁闷!!
首先想一维的情况,next[n]表示最大整数满足前缀1…next[n] 与 后缀n-next[n]+1 … n相等,那么对于一维的情况 我猜想最小面积的覆盖单元是子串1 … n-next[n]
举例:
abcd abcd abcd abc ,next[n]为11 最小覆盖单元为abcd 即1…(15-11)
acbd efghijklmn abcd next[n]为4 最小覆盖单元为abcd efghijklmn 即1… (18-4)
证明一下:首先证明1… n-next[n]是可以覆盖整个字符串的,因为剩下部分n-next[n]+1…n可以由1…next[n]覆盖所以可以覆盖的,然后证明没有比这个短的前缀字符串可以覆盖整个字符串:
假设存在一个子字符串[1,3,2]能覆盖整个字符串,1,2,3分别代表字符串,假设这个串只循环了一次,整个串可以表示为:(2可以理解成[1,3,2]用来覆盖首部,1可以理解成[1,3,2]用来覆盖尾部)
[2,1,3,2,1] next[n]的长度应为串2和串1的和,n-next[n]就刚好为[1,3,2]的长度
假设这个串循环了k次即:
[2,1,3,2,1,3,2,…,1,3,2,1] next[n]就为2,1,3,2,…,3,2,1 即2的长度加上k-1个1,3,2再加上一个1,n-next[n]就刚好为[1,3,2]的长度 综上以上情况可知对于1n的字符串 n-next[n]为最小覆盖该字符串的子字符串的长度
对于2
n的字符矩阵,第一行的next[n]为a,第二行的next[n]为b,单单覆盖第一行 只需要n-a的长度的字符串,单单覆盖第二行 只需要n-b长度的字符串
我的证明不够严谨,没看懂的话,移步到下面这个博客
https://blog.csdn.net/fjsd155/article/details/6866991
对于证明n-next[n]是可以覆盖整个字符串的 我只证明了 next[n] < n - next[n]的情况,当next[n] >= n - next[n]的时候 证明见博客
光盘里面的代码:

//Author:XuHt
#include <cstdio>
#include <cstring>
#include <iostream>
#define ull unsigned long long
using namespace std;
const int R = 10006, C = 81, P = 13331;
int r, c, nxt[R];
char s[R][C];
ull H[R];

int work(int len) {
	int i = 0, j = -1;
	nxt[0] = -1;
	while (i < len)
		if (j == -1 || H[i] == H[j]) nxt[++i] = ++j;
		else j = nxt[j];
	return len - nxt[len];
}

int main() {
	cin >> r >> c;
	for (int i = 0; i < r; i++) scanf("%s", s[i]);
	memset(H, 0, sizeof(H));
	for (int i = 0; i < r; i++)
		for (int j = 0; j < c; j++)
			H[i] = H[i] * P + s[i][j];
	int ans = work(r);
	memset(H, 0, sizeof(H));
	for (int i = 0; i < c; i++)
		for (int j = 0; j < r; j++)
			H[i] = H[i] * P + s[j][i];
	ans *= work(c);
	cout << ans << endl;
	return 0;
}

思路是计算每行的字符串hash值 然后把整个行当做一个字符,然后按垂直方向进行KMP算法,得到高
接着,计算每列的字符串的hash值 然后把整个列当做一个字符,然后按水平方向进行KMP算法,得到宽 结果是高乘上宽

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值