ACM实训【ZOJ/POJ】

专题1:输入输出    ZOJ 1045、ZOJ 1383
专题2:STL    ZOJ 1649、ZOJ 1965
专题3:线段树    ZOJ 1610、ZOJ 2451
专题4:并查集    ZOJ 1789、ZOJ 2833
专题5:四分树    ZOJ 1788
专题6:最小生成树    ZOJ 1203
专题7:拓扑排序    ZOJ 1083
专题8:最短路径    ZOJ 1942、POJ 1860
专题9:二部图    ZOJ 1654
专题10:搜索算法    ZOJ 1091、ZOJ 1204、ZOJ 1217、ZOJ 1649

ZOJ 1045

#include<iostream>
using namespace std;

int main(){
	double a, sum;
	while(cin >> a && a){
		sum = 0;
		for(int i=1; ; i++){
			sum += 1.0/(i+1);
			if(sum >= a){
				cout << i << " card(s)" << endl;
				break;
			}
		}
	}
	return 0;
}

ZOJ 1383

#include<iostream>
using namespace std;

int main(){
    int d;
    cin >> d;
    int data, count;
    for(int n=0; n<d; n++) {
        cin >> data;
        count = 0;
        while(data != 0) {
            if(data%2 == 1) {
                cout << count;
                if(data/2 != 0) cout << " ";
            }
            data = data/2;
            count++;
        }
        cout << endl;
    }
    return 0;
}

ZOJ 1649

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

int n, m;

struct node {
	int x, y;
	int time;//步行时间 
}start, end;

bool operator<(const node& a, const node& b) {
	return a.time > b.time;
}

char map[300][300];
int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

bool Judge(int x, int y) {
	if(x < 0 || y < 0 || x >= n || y >= m) return false;
	if(map[x][y] == '#') return false;
	if(map[x][y] == 'r') return false;
	return true;
}

int BFS() {
	priority_queue<node> q;
	node temp, newpos;
	q.push(start);
	while(!q.empty()) {
		temp = q.top();
		q.pop();
		for(int i=0; i<4; i++) {
			newpos.x = temp.x+dir[i][0];
			newpos.y = temp.y+dir[i][1];
			newpos.time = temp.time;
			if(Judge(newpos.x, newpos.y)) {
				if(map[newpos.x][newpos.y] == 'a') return temp.time+1;
				if(map[newpos.x][newpos.y] == 'x') newpos.time = newpos.time+2;
				else if(map[newpos.x][newpos.y] == '.') newpos.time = newpos.time+1;
				q.push(newpos);
				map[newpos.x][newpos.y] = '#';
			}
		}
	}
	return -1;
}

int main() {
	while(scanf("%d%d", &n, &m) != EOF) {
		getchar();
		for(int i=0; i<n; i++) {
			for(int j=0; j<m; j++) {
				scanf("%c", &map[i][j]);
				if(map[i][j] == 'r') {
					start.x = i;
					start.y = j;
				}
			}
			getchar();
		}
		int ans = BFS();
		if(ans != -1) printf("%d\n", ans);
		else printf("Poor ANGEL has to stay in the prison all his life.\n");
	}
	return 0;
}

ZOJ 1965

#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
#include<string>
#include<sstream>
#include<algorithm>
#include<vector>
using namespace std;

//会出现n==1即输入数据(n-1)只有一个回车的情况
//所以不能用scanf或者getchar,可以用sstream类库读取数据

int f[55], ans[55];
vector<int> vec[55];

bool CMP(const int &a, const int &b) {
	return a < b;
}

void DFS(int x) {
	printf("(");
	printf("%d",x);
	int l = vec[x].size();
	sort(vec[x].begin(), vec[x].end(), CMP);//vector排序
	for(int i=0; i<l; i++) {
		printf(" ");
		DFS(vec[x][i]);
	}
	printf(")");
	vec[x].clear();//清空vector

int main() {
	int i, n = 0;
	char c;
	string line;
	memset(ans, 0, sizeof(ans));
	while(getline(cin, line)) {
		stringstream ss(line);
		for(n=0; ss>>f[n]; n++) {
			ans[f[n]]++;
		}
		n--;
		priority_queue<int, vector<int>, greater<int>> leafs;
		for(i=1; i<=n+1; i++) {
			if(!ans[i]) leafs.push(i);
		}
		for(i=0;i<=n;i++) {
			int k = leafs.top();
			leafs.pop();
			vec[f[i]].push_back(k);
			if(--ans[f[i]] == 0) leafs.push(f[i]);
		}
		if(n != -1) DFS(f[n]);
		else printf("(1)");//输入为0
		printf("\n");
		memset(ans, 0, sizeof(ans));
	}
}

ZOJ 1610

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int max = 8010;
const int nocolor = -1;//区间未被染色 
const int divide = -2;//区间被拆分染色 

int color[8010], ans;

struct Node {
	int left, right;
	int color;
}tree[33000];

//建立线段树 
void BuildTree(int i, int l, int r) {
	tree[i].left = l;
	tree[i].right = r;
	tree[i].color = nocolor;
	if(tree[i].right-tree[i].left == 1) return;
	int mid = (l+r)>>1;
	BuildTree(i*2, l , mid);
	BuildTree(i*2+1, mid, r);
}

//将区间(l,r)染色c 
void UpdateColor(int i, int l, int r, int c) {
	//已被染上该色
	if(tree[i].color == c) return;
	//将区间染色
	if(l == tree[i].left && r == tree[i].right) {
		tree[i].color = c;
		return;
	}
	//若区间未被拆分,将其拆分并将子区间染色 
	if(tree[i].color != divide) {
		tree[i*2].color = tree[i].color;
		tree[i*2+1].color = tree[i].color;
		tree[i].color = divide;
	}
	
	int mid = (tree[i].left+tree[i].right)/2;
	//区间(l,r)在左子区间内 
	if(r <= mid) {
		UpdateColor(i*2, l, r, c);
		return;
	}
	//区间(l,r)在右子区间内 
	if(l >= mid) {
		UpdateColor(i*2+1, l, r, c);
		return;
	}
	//区间(l,r)跨越了当前区间的中点 
	UpdateColor(i*2, l, mid, c);
	UpdateColor(i*2+1, mid, r, c);
}

//统计区间(l,r)上的颜色
void CountColor(int i) {
	if(tree[i].color == nocolor) {
		ans = -1;
		return;
	}
	if(tree[i].color != divide) {
		if(tree[i].color != ans) {
			color[tree[i].color]++;
			ans = tree[i].color;//ans标记当前颜色,防止连续线段计成多段
		}
		return;
	}
	if(tree[i].right-tree[i].left != 1) {
		CountColor(i*2);
		CountColor(i*2+1);
	}
} 

int main() {
	int num, l, r, c, maxcolor;
	while(~scanf("%d", &num)) {
		BuildTree(1, 0, 8000);
		maxcolor = 0;
		while(num--) {
			scanf("%d%d%d", &l, &r, &c);
			UpdateColor(1, l, r, c);
			if(c > maxcolor) maxcolor = c;
		}
		ans = -1;
		memset(color, 0, sizeof(int)*(maxcolor+1));
		CountColor(1);
		for(int i=0; i<=maxcolor; i++) {
			if(color[i]) printf("%d %d\n", i, color[i]);
		}
		printf("\n");
	}
	return 0;
}

ZOJ 2451

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int inf = 1000000;
int tree[201000];

void BuildTree(int i, int left, int right) {
	tree[i] = inf; 
	if(left == right) return;
	BuildTree(i*2, left, (left+right)/2);
	BuildTree(i*2+1, (left+right)/2+1, right);
}

//实时更新线段树,使得查询时各节点的tree[i]是最小值
//i线段树的节点序号,pos已排序区间的端点值,val到达pos所需最少的sorter数 
void UpdateTree(int i, int pos, int val, int left, int right) {
	//若新的val更小,更新
	if(tree[i] > val) tree[i] = val;
	//返回点线段的状态 
	if(left == right) return;
	//向下更新 
	int mid = (left+right)/2;
	if(pos <= mid) UpdateTree(i*2, pos, val, left, mid);
	else UpdateTree(i*2+1, pos, val, mid+1, right);
} 

//查询新输入的x~y区间中可以连接新sorter的最小值并返回
//x~y是新的sorter区间,left~right是线段树的跨度
int Inquiry(int i, int x, int y, int left, int right) {  
	if(x <= left && y >= right) return tree[i];
	//查询子区间 
	int ans1 = inf, ans2 = inf;
	int mid = (left+right)/2;
	if(x <= mid) ans1 = Inquiry(i*2, x, y, left, mid);
	if(y > mid) ans2 = Inquiry(i*2+1, x, y, mid+1, right);
	return ans1 < ans2 ? ans1 : ans2;
}  


int main() {
	int n, m;
	while(~scanf("%d%d", &n, &m)) { 
		BuildTree(1, 1, n);
		//线段树中所有区间端点值为1的tree[i]=0
		UpdateTree(1, 1, 0, 1, n);
		while(m--) {
			int x, y;
			scanf("%d%d", &x, &y);
			int temp = Inquiry(1, x, y, 1, n);   
			UpdateTree(1, y, ++temp, 1, n);
		}
		printf("%d\n", Inquiry(1, n, n, 1, n));	
	}
	return 0;
}

ZOJ 1789

#include<iostream>
#include<cstring>
using namespace std;

int parent[30002];
int n, m;

int Find(int x) {
    int r = x;
    while(parent[r] >= 0) {
        r = parent[r];
    }
    while(x != r) {
        int ans = parent[x];
        parent[x] = r;
        x = ans;
    } //路径压缩
    return r;
}

void Union(int x, int y) { //降维合并
    int findx, findy;
    findx = Find(x);
    findy = Find(y);
    int temp = parent[findx] + parent[findy];
    if(findx != findy) {
        if(parent[findx] > parent[findy]) {
            parent[findy] = temp;
            parent[findx] = findy;
        }
        else {
            parent[findx] = temp;
            parent[findy] = findx;
        }
    }
}

int main() {
    cin >> n >> m;
    while(n != 0 || m != 0) {
        memset(parent, -1, sizeof(parent));
        while(m--) {
            int num, a, b;
            cin >> num >> a;
            while(--num) {
                cin >> b;
                Union(a, b);
            }
        }
        cout << -parent[Find(0)] << endl;
        cin >> n >> m;
    }
    return 0;
}

ZOJ 2833

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int parent[100010];

//找包含x的朋友圈的根i 
int Find(int x) {
	int i = x;
	//找包含x的朋友圈的根i 
	while(parent[i] >= 0) {
		i = parent[i];
	} 
	//路径压缩,将x的朋友、x的朋友的朋友...表示为i的朋友 
	while(x != i) {
		int temp = parent[x];
		parent[x] = i;
		x = temp;
	}
	return i;
}

//x和y成为朋友 
void Merge(int x, int y) {
	int fx, fy;
	fx = Find(x);//找包含x的朋友圈的根fx 
	fy = Find(y);//找包含y的朋友圈的根fy 
	//朋友圈的根记录着朋友圈内人数的负数 
	int temp = parent[fx] + parent[fy]; 
	//x和y原先不在同一个朋友圈,合并朋友圈 
	if(fx != fy) {
		//x所在朋友圈合并到y所在的朋友圈 
		if(parent[fx] > parent[fy]) {
			parent[fy] = temp;
			parent[fx] = fy;
		}
		//y所在朋友圈合并到x所在的朋友圈
		else {
			parent[fx] = temp;
			parent[fy] = fx;
		}
	}
}

int main() {
	int n, m, a, b;
	int num = 0;
	while(scanf("%d%d", &n, &m) != EOF) {
		memset(parent, -1, sizeof(int)*(n+1));
		if(num) printf("\n");
		printf("Case %d:\n", ++num);
		while(m--) {
			char c;
			cin >> c;
			//a和b成为朋友 
			if(c == 'M') {
				scanf("%d%d", &a, &b);
				Merge(a, b);
			}
			//通过查询a所在的朋友圈得到a的朋友数量 
			else if(c == 'Q') {
				scanf("%d", &a);
				printf("%d\n", -parent[Find(a)]);
			} 
		}
	}
	return 0;
}

ZOJ 1788

#include<iostream>
#include<cstring>
#include<cstdio> 
#include<queue>
using namespace std;
 
class quadtree {
	public:
		char value[3];//00,01,1(00表示全0, 01表示全1, 1表示mixed) 
		quadtree *child[4];
		quadtree() { 
			child[0] = child[1] = child[2] = child[3] = 0;
		} 
		bool operator==(const quadtree& p) const {
			//如果有孩子值为1或者存在相异值则不能合并 
			if(strcmp(value, "1") == 0 || strcmp(value, p.value) != 0) return 0;  
			else return 1;
		}
}; 
 
quadtree *head;
char map[520][520], ans[10000], str[5];
int N, a[2500];
  
void init() {  
	scanf("%d", &N);
	for(int i=0; i<N; i++) {
		for(int j=0; j<N; j++) {
			cin >> map[i][j];
		}
	}	
	str[4] = 0;
	memset(ans, 0, sizeof(ans));
} 
 
//四分树的四分,合并,剪枝,深度优先 
quadtree * DFS(int r, int c, int len) {
	quadtree*temp = new quadtree;
	//四分到最小格
	if(len == 1) {  
		temp->value[0] = '0';//第一个数置0 
		temp->value[1] = map[r][c];//第二个数则为方格中的数字 
		temp->value[2] = 0;//最后一位置0
		return temp;
	}
	//将map四分
	len /= 2;   
	temp->child[0] = DFS(r, c, len);
	temp->child[1] = DFS(r, c+len, len);
	temp->child[2] = DFS(r+len, c, len);
	temp->child[3] = DFS(r+len, c+len, len);
	//判断是否符合合并要求 
	bool flag = true;
	for(int i=1; i<4; i++) {
		if(!(*temp->child[0] == *temp->child[i])) { 
			flag = false;
			break;
		}
	} 
	//满足要求则合并
	if(flag) {
		strcpy(temp->value, temp->child[0]->value);
		for(int i=0; i<4; i++) {
			delete temp->child[i]; 
			temp->child[i] = 0;
		}
	}
	else strcpy(temp->value, "1");//否则不合并,且节点值为1 
	return temp;
}
 
//将二进制转化为十进制数
void funtion(char s[]) {
	int sum = 0;
	for(int i=0; i<4; i++) {
		sum = sum*2+str[i];
	}
	printf("%X", sum);
}
 
//层次遍历取出二进制数字字符串,并转为十六进制输出
void print() {
	int i, j, m;
	quadtree *temp;
	queue<quadtree *> q;
	q.push(head);
	while(!q.empty()) {
		temp = q.front();
		q.pop();
		//将二进制串连接起来
		strcat(ans, temp->value);
		//若该节点存在孩子
		if(!(temp->child[0] == 0)) {
		 	for(i=0; i<4; i++) {
		 		q.push(temp->child[i]);
			 }
		 }
		delete temp; 
	}
	int slen =strlen(ans);//计算ans的长度 
	int pos=0;//标记ans当前计算的位置 
	//为了方便计算,将整个数字串长度补成4的倍数
	//用长度求余数得i,在最前面补4-i个0
	if(slen%4 != 0) {
		i = slen%4;
		for(j=0; j<4-i; j++) {
			str[j] = 0;
		}
		for(m=j; m<4; m++) {
			str[m] = ans[pos++]-'0';
		} 
		funtion(str);
	}
	for(i=pos; i<slen; i+=4) {
		for(j=0; j<4; j++) {
			str[j] = ans[i+j]-'0';
		}
		funtion(str);
	}
	printf("\n");
} 
 
int main() {
	int num;
	scanf("%d", &num);
	while(num--) {
		init();
		head = DFS(0, 0, N);
		print();
	}
	return 0;
}

ZOJ 1203

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

double dis[110][110], pos[110][2], val[110], vis[110];
double ans;
int n;

void MakeDis() {
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            dis[i][j] = sqrt((pos[i][0]-pos[j][0])*(pos[i][0]-pos[j][0])+(pos[i][1]-pos[j][1])*(pos[i][1]-pos[j][1]));
        }
    }
}

void Prim() {
    memset(vis, 0, sizeof(vis));
    //访问第一个点 
    vis[1] = 1;
    int k;
    double min, ans = 0;
    //计算从第一个点出发的所有边 
    for(int i=1; i<=n; i++) {
        val[i] = dis[1][i];
    }
    //要对剩下的所有点进行操作,循环总计n-1次 
    for(int i=2; i<=n; i++) {
        k = 1; min = 10000;
        //找到当前点出发的最短边,访问点k 
        for(int j=1; j<=n; j++) {
            if(!vis[j] && val[j] < min) {
                min = val[j];
                k = j;
            }
        }
        vis[k] = 1;
        //记录当前距离 
    	ans = ans+min;
    	//根据新访问的点到剩下未被访问的点的距离,更新最短距离 
    	for(int j=1; j<=n; j++) {
        	if(!vis[j] && val[j] > dis[j][k]) {
        	    val[j] = dis[j][k];
        	}
    	}
    }
    printf("The minimal distance is: %.2lf\n", ans);
}

int main() {
    int t, step = 0;
    while(scanf("%d", &n)) {
        step++;
        memset(pos, 0, sizeof(pos));
        memset(dis, 0, sizeof(dis));
        memset(val, 0, sizeof(val));
        ans = 0;
        if(n == 0) break;
        if(step != 1) printf("\n");
        for(int i=1; i<=n; i++) {
            scanf("%lf%lf", &pos[i][0], &pos[i][1]);
        }
        MakeDis();
        printf("Case #%d:\n", step);
        Prim();
    }
    return 0;
}

ZOJ 1083

题目描述:每张图片由同一个字母组成的边框表示,不同图片的字母都不同。在一个区域上放置这些区域,区域最大为30*30。每张图片的四条边都一定会有字符能够显示。我们需要通过图片叠加的最终结果得到从底层到顶层的图片次序,有多种可能性时按字典序输出。

解题思路:本题的任务是推出图片的叠加次序。因此,第一步是根据整幅图的字母位置还原出每个边框的边界位置。因为每个字母只能出现在一副图片上,所以使用一个letter数组来记录字母是否已经被使用了。增加一个map数组,map[x][y]表示字母y所在边框位于字母x所在边框之上。再增加一个in数组,它记录每个字母的入度。在此基础上就可以建立拓扑排序图:遍历26个字母,假设字母1被使用了,则判断它所在的边框上是否有被其他字母覆盖,若字母2覆盖在字母1所在边框上,则字母2的入度加一,并建立由字母1指向字母2的有向边。最后根据记录入度的数组,输出最终结果。入度越低,字母所在边框的位置就越靠近底部,所以要从入度为0的点开始输出。每输出一个点就要删除该点,并删除所有从它出发的边,有向边的终点的入度减一。

程序清单如下

解题总结:特别注意,题目要求的输出顺序是从下到上,且可能有多种结果,均需要输出,可以选择递归的方式,总共递归次数要等于被使用了的字母的个数,每次都用vector记录下结果。与此同时,理解题意仍然是最困难的部分。而且很难想到根据拓扑排序求解,突破点应是有前后顺序要求的有向图。不过把叠加的层次转换成有向图也不是那么容易想到,因为始终将它看作是一个简单的平面图,然而它却是一个叠加了多层图片的立体架构。要是想到从该架构的侧面入手,就不难将它转换为有向图了。

ZOJ 1942 作业报告

题目描述:青蛙从起点到达终点的某条路径上,某石头到另一块石头的距离是跳跃的步长,而路径中最长的步长代表了青蛙走该条路径时需要的跳跃能力。我们需要找到跳跃能力的最小值,也就是说青蛙要想到达目的地,跳跃能力最小要为多少?

解题思路:本题的任务是①找到石头1到石头2的通路,②比较每条通路中距离最大的那一段,找到最短的。因此,首先需要以两点之间的欧氏距离为权值构造一个带权图。石头x到石头y间目前已知的所需跳跃能力存储在Distance数组里面,初始数值为两石头间距。源点为石头1,集合stone被划分为集合visit和集合stone-visit两个部分,初始visit中仅含有石头1。【在集合stone-visit中找到距离石头1最近的石头,访问它并将其加入visit中。然后化用求解最短路径的方法,将该点作为中转站,比较目前已知的所需跳跃能力与经过中转站得到的所需跳跃能力,根据修改规则修改石头1到达stone-visit中每个石头的所需跳跃能力。假设要从源点到石头v,中间经过石头w,从石头1到达石头v所需的跳跃能力的修改规则为 Distance(1, v) = min(Distance(1, v), max(Distance(1, w), Distance(w, v)))。】重复上述部分,知道所有的石头都被访问过了。最终Distance(1, 2)即为答案。

程序清单如下

​​​​ ​​​​​​解题总结:在这道题目中,读懂题目并且理解题意可以算是最困难的地方,在题干比较绕时,不一定要死读题目,还可以从输入输出的例子下手,通过观察它们的关系,反推回去就能够比较容易地搞清楚题目的任务。除此之外,另一个难点就是将Dijkstra算法变形以求解最终结果。这道题是单源路径问题,由此可以作为解题的一个突破口,然后简化题目,对两到三个点进行手工计算,计算过程能够发现近似于动态规划过程,并找到比较与修改的规则。由此就能顺利地完成代码。

POJ 1860

题目描述:给出若干种货币,某两种货币间可以互相兑换,兑换时需要考虑汇率并支付一定的佣金。现在有一些货币S,已知初始数额,需要我们判断是否能通过一些兑换操作,使得最后货币的种类依旧是S,且货币的数额大于初始数额?

解题思路:本题的任务是找到一个环,货币的数额经过这个环后增大。求解的第一步,我们需要根据给出的货币种类与兑换条件,将所有信息转化为图:把货币的种类作为图的顶点,将兑换过程作为顶点间动态的有向边,也就是说,边的权值不是固定的数值,而是基于函数关系,也就是汇率与佣金动态变化的。接着使用Bellman Ford算法判断是否存在正环,经过最多n-1次松弛还能继续松弛则存在正环,通过这个正环可以使得货币金额不断增多。松弛条件为货币的数额增加。循环过程中,若无法继续松弛下去,则说明不存在正环。况且这道题目的兑换都是双向的,因此只要有正环存在,那么可以通过正环使得货币无限增加,在这种情况下就算是起点没有包含在环中,按照原来的路径也可以换回起点货币。

程序清单如下

​​​​​​解题总结:第一次写这道题的时候,由于没有认真阅读题意,不清楚最后的货币类别需要是初始货币类别,就直接考虑了广度优先算法和深度优先算法,以此寻找一条能使货币数额增加的路径。看了别人的代码才发现理解有误,原本的做法虽然也使货币数额增大了,但是最后兑换回去可能因为汇率与佣金问题又再次减少。于是改用了Bellman Ford算法。比较难理解的是为什么只需要正环存在就能使数额增加,仔细理解了一下,只要存在正环,货币数额就能无限增长,且支持双向兑换就必定能换回初始货币,就算在兑换回去的时候有那么一些损失,但是在正环中已经无限增长,损失可以忽略不计。

ZOJ 1654

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n, m;
char map[51][51];
int x[51*51], y[51*51];//xi匹配的y顶点,yi匹配的x顶点
int xno[51][51], yno[51][51];//水平/垂直方向上块的编号
int xn, yn;//水平/垂直方向上块的个数
bool g[51*51][51*51];//水平上第i块与垂直上第j块是否有公共区域 
int visit[51*51];

//从x中的点u出发用深度优先策略寻找增广路 
int Path(int u) {
	//考虑y中的所有点 
	for(int v=1; v<=yn; v++) {
		//v跟u邻接并且v未访问过,访问 
		if(g[u][v] && !visit[v]) {
			visit[v] = 1;
			//v未匹配,或者v已经匹配了但从y[v]出发可以找到一条增广路
			if(!y[v] || Path(y[v])) {
				//更新匹配 
				x[u] = v;
				y[v] = u;
				return 1;
			}
		}
	}
	//不存在从u出发的增广路
	return 0;
}

//最大匹配算法 
void MaxMatch() {
	int ans = 0;
	memset(x, 0, sizeof(x));
	memset(y, 0, sizeof(y));
	//从每个未饱和点出发寻找增广路
	for(int i=1; i<=xn; i++) {
		if(!x[i]) {
			memset(visit, 0, sizeof(visit));
			if(Path(i)) ans++; 
		}
	}
	printf("%d\n", ans);
}

int main() {
	int kase, num, flag;
	scanf("%d", &kase);
	for(int k=0; k<kase; k++) {
		scanf("%d%d", &m, &n);
		memset(xno, 0, sizeof(xno));
		memset(yno, 0, sizeof(yno));
		memset(g, 0, sizeof(g)); 
		for(int i=0; i<m; i++) {
			scanf("%s", map[i]);
		}
		//对水平方向上的块进行编号
		num = 0;
		for(int i=0; i<m; i++) {
			flag = 0;
			for(int j=0; j<n; j++) {
				if(map[i][j] == 'o') {
					if(flag == 0) num++;
					xno[i][j] = num;
					flag = 1;
				}
				else if(map[i][j] == '#') flag = 0;
			} 
		}
		xn = num;
		//对垂直方向上的块进行编号
		num = 0;
		for(int j=0; j<n; j++) {
			flag = 0;
			for(int i=0; i<m; i++) {
				if(map[i][j] == 'o') {
					if(flag == 0) num++;
					yno[i][j] = num;
					flag = 1;
				}
				else if(map[i][j] == '#') flag = 0;
			}
		}
		yn = num;
		//连接有公共区域的水平上的块和垂直上的块 
		for(int i=0; i<m; i++) {
			for(int j=0; j<n; j++) {
				if(xno[i][j] && yno[i][j]) g[xno[i][j]][yno[i][j]] = 1;  
			}
		}
		printf("Case :%d\n", k+1);
		//寻找最大匹配 
		MaxMatch();
	}
	return 0;
}

ZOJ 1091

#include <iostream>
#include <cstring>
using namespace std;

int step1[8] = {1,1,2,2,-1,-1,-2,-2};
int step2[8] = {2,-2,1,-1,2,-2,1,-1};
int knight[8][8];
 
void Find(int st1, int st2, int moves) {
    if(st1 < 0 || st1 > 7 || st2 < 0 || st2 > 7 || moves >= knight[st1][st2]) {
		return;
	}
    knight[st1][st2] = moves;
    for(int m = 0; m < 8; m++) {
        Find(st1 + step1[m], st2 + step2[m], moves + 1);
    }
}

int main() {
    string a, b;
    while(cin >> a >> b) {
        memset(knight, 100, sizeof(knight));
        Find(a[0] - 'a', a[1] - '1', 0);
        cout << "To get from " << a << " to " << b << " takes " << knight[b[0] - 'a'][b[1] - '1'] << " knight moves." << endl;
    }
    return 0;
}

ZOJ 1204

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

int n, a[30], visit[30];
bool flag;//判断是否有解(即满足题意的等式)

void DFS(int start, int depth, int sum) {
    if (depth == 0) { // 已经到底了
        for (int i = start; i < n && sum >= a[i]; i++) {
            if (sum == a[i]) {
                flag = true;
                for (int j = 0; j <= i; j++) {
                    if (visit[j]) {
                        if (sum == a[j]) {
                            //说明a[j]是等式左边最后一个加数了,下面每次sum都减去加数就是为了方便判断
                            cout << a[j] << "=" << a[i] << endl;
                        }
                        else cout << a[j] << "+";
                        sum -= a[j];
                    }
                }
            }
        }
    }
    else { //还没到底,继续深搜
        for (int i = start; i < n; i++) {
            if (sum + a[i] <= a[n - 1]) {
                sum += a[i];
                visit[i] = 1;
                --depth;
                DFS(i + 1, depth, sum);
                sum -= a[i];
                visit[i] = 0;
                ++depth;
            }
        }
    }
}

int main(void) {
    int N;
    cin >> N;
    while(N--) {
        cin >> n;
        for(int i = 0; i < n; i++) {
        	cin >> a[i];
		}
        sort(a, a+n);
        memset(visit, 0, sizeof(int)*30);
        flag = false;
        for(int i=2; i<n; i++) {//深搜深度范围为[2,n-1](深度即equation左边的数字个数)
			DFS(0, i, 0);
		}
        if(!flag) cout << "Can't find any equations." << endl << endl;
        else cout << endl;
    }
    return 0;
}

ZOJ 1217

#include<iostream>
#include<string>
#include<map>
#include<iterator>
#include<queue>
using namespace std;
 
map<string,string> m;  //记录状态转换图,前后转换关系形成m映射 
map<string,string>::iterator it;
queue<string> q;//广搜队列 
string goal = "12345678x"; 

void Map(string s1,string s2,int t1,int t2){
	s2 = s1; 
	s1[t2] = 'x';
	s1[t1] = s2[t2];//建立新的状态字符串s1 
	it = m.find(s1);
	//如果m中没有s1状态,则将其加入m 
	if(it == m.end()) {
		m[s1] = s2;//s1是由s2转换来的 
		q.push(s1);//将当前搜索到的状态放入队列 
	} 	
}
 
void Search(){
	//初始化 
	string s1 = goal, s2;
	q.push(s1);
	m[s1] = s1;//方便最后的判断,直接判映射值和goal是否相同即可 
	int t1, t2;
	while(!q.empty()) {
		//取出待搜索状态
		s1 = q.front(); 
		q.pop(); 
		//查找x的位置,用t1记录 
		for(t1=0; t1<9; t1++) {
			if(s1[t1] == 'x') break;
		}
		//不是最上面的一列,可以上移
		if(t1/3 != 0) { 
			t2 = t1-3; 
			Map(s1, s2, t1, t2); 
		}  
		//不是最下面的一列,可以下移
		if(t1/3 != 2) { 
			t2 = t1+3; 
			Map(s1, s2, t1, t2); 
		}  
		//不是最左边的一列,可以左移
		if(t1%3 != 0) { 
			t2 = t1-1; 
			Map(s1, s2, t1, t2); 
		}  
		//不是最右边的一列,可以右移
		if(t1%3 != 2) { 
			t2 = t1+1; 
			Map(s1, s2, t1, t2); 
		}  	 
	}
}
 
int main(){
	int ans1, ans2;
	string s, ms;
	char c;
	m.clear();
	Search();//记录状态转换图 
	while(cin >> c) {
		//输入初始状态s 
		s = "";
		s += c;
		for(int i=1; i<9; i++) { 
			cin >> c; 
			s += c;
		}
		//在m中找s 
		it = m.find(s);
		//在m中没有找到s,unsolvable
		if(it == m.end()) cout << "unsolvable" << endl; 
		//在m中找到s 
		else {
			while(s != goal) {
				it = m.find(s);
				ms = it->second;//first得到key,second得到value
				//比较s和ms,通过x的位置变化判断u/d/r/l 
				for(ans1=0; ans1<9; ans1++) {
					if(s[ans1] == 'x') break;
				}
				for(ans2=0; ans2<9; ans2++) {
					if(ms[ans2] == 'x') break;
				}
				int ans = ans2-ans1;
				if(ans == -3) cout << "u";
				else if(ans == 3) cout << "d";
				else if(ans == 1) cout << "r";
				else if(ans == -1) cout << "l";
				//s变化为ms,继续搜索 
				s = ms;
			}
			cout << endl;
		}
	}
	return 0;
} 

伸展树 课件题目

#include<iostream>
#include<stdio.h>
using namespace std;

const int maxn = 1e5 + 99;
const int INF = 0x3f3f3f3f;

//不断插入节点 
//将新插入的节点调整为根节点
//比较与左孩子和右孩子的差值,较小的即为最小差值 
struct SpalyTree {
    int ch[maxn][2], pre[maxn], sz[maxn], key[maxn], root, tot1;
    
    void Rotate(int x, int d) {
        int y = pre[x];
        ch[y][!d] = ch[x][d];
        pre[ch[x][d]] = y;
        if (pre[y]) ch[pre[y]][ch[pre[y]][1] == y] = x;
        pre[x] = pre[y];
        ch[x][d] = y;
        pre[y] = x;
    }

    void Splay(int r, int goal) {
        while (pre[r] != goal) {
            if (pre[pre[r]] == goal) Rotate(r, ch[pre[r]][0] == r);
            else {
                int y = pre[r];
                int d = ch[pre[y]][0] == y;
                if (ch[y][d] == r) {
                    Rotate(r, !d);
                    Rotate(r, d);
                }
                else{
                    Rotate(y, d);
                    Rotate(r, d);
                }
            }
        }
        if(goal == 0) root = r;
    }

    void Newnode(int &r, int fa, int k) {
        r = ++tot1;
        sz[r] = 1;
        key[r] = k;
        pre[r] = fa;
        ch[r][0] = ch[r][1] = 0;
    }
    
    int Insert(int k) {
        int r = root;
        while(1) {
            if(key[r] == k) {
                Splay(r, 0);
                return 0;
            }
            else if (k < key[r]) {
                if(ch[r][0]) r = ch[r][0];
                else {
                    Newnode(ch[r][0], r, k);
                    Splay(ch[r][0], 0);
                    return 1;
                }
            }
            else if(k > key[r]) {
                if(ch[r][1]) r = ch[r][1];
                else {
                    Newnode(ch[r][1], r, k);
                    Splay(ch[r][1], 0);
                    return 1;
                }
            }
        }
    }

    int GetPre(int x) {
        int r = ch[x][0];
        if(r == 0) return INF;
        while(ch[r][1]) {
        	r = ch[r][1];
		}
        return key[x]-key[r];
    }

    int GetNext(int x) {
        int r = ch[x][1];
        if(r == 0) return INF;
        while(ch[r][0]) {
        	r = ch[r][0];
		}
        return key[r]-key[x];
    }


}tree;

int main() {
    int n;
    while(scanf("%d", &n) == 1) {
        tree.root = tree.tot1 = 0;
        int ans = 0;
        for(int i=1; i<=n; i++) {
            int num;
            if(scanf("%d", &num) == EOF) num = 0;
            if(i == 1) {
                ans += num;
                tree.Newnode(tree.root, 0, num);
                continue;
            }
            if(tree.Insert(num) == 0) continue;
            int a = tree.GetPre(tree.root);
            int b = tree.GetNext(tree.root);
            ans += min(a, b);
        }
        printf("%d\n", ans);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值