Noip2003提高组

又逢运动会,又是一个在机房刷题的日子。


t1.神经网络

题目

题意:略。

思路:[拓扑排序+模拟] 先处理出一个拓扑序,然后直接模拟即可。

Code:

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
//Mystery_Sky
//
#define M 1000100
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

struct Edge{
	int to, next, w;
}edge[M];
int n, m;
int c[M], u[M], pos[M];
int cnt, head[M], dis[M], out[M], ind[M];
queue <int> q;
vector <int> t;

inline void add_edge(int u, int v, int w)
{
	edge[++cnt].to = v;
	edge[cnt].next = head[u];
	edge[cnt].w = w;
	head[u] = cnt;
}

inline void topo()
{
	for(int i = 1; i <= n; i++) 
		if(!ind[i]) q.push(i), pos[i] = 1;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		t.push_back(u); 
		for(int i = head[u]; i; i = edge[i].next) {
			int v = edge[i].to;
			ind[v]--;
			if(!ind[v]) q.push(v); 
		}
	} 
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= n; i++) c[i] = read(), u[i] = read();
	for(int i = 1; i <= m; i++) {
		int u = read(), v = read(), w = read();
		add_edge(u, v, w);
		ind[v]++;
		out[u]++;
	}
	topo();
	for(int a = 0; a < n; a++) {
		int i = t[a];
		if(pos[i] != 1) c[i] -= u[i];
		if(c[i] > 0) {
			for(int x = head[i]; x; x = edge[x].next) {
				int v = edge[x].to;
				c[v] += edge[x].w * c[i];
			}
		}
	}
	bool flag = false;
	for(int i = 1; i <= n; i++) {
		if(out[i] == 0 && c[i] > 0) printf("%d %d\n", i, c[i]), flag = true;
	}
	if(!flag) printf("NULL\n");
	return 0;
}

t2.侦探推理

题目

题意:有M个人,其中N个人只说谎话,其余的人只说实话,有P条证词。然后根据证词找出罪犯。

思路:[模拟] 不会,过

Code:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <ctime> 
//Mystery_Sky
//QWQ
using namespace std;
int main (){
    srand(time(0));
    if(rand()%2) printf("Cannot Determine\n");
    else printf("Impossible\n");
    return 0;
}

t3.加分二叉树

题目

题意:自行领悟,略。

思路:[dp] 之前在外面培训的时候讲过,然而我好像忘了。设f[i][j]表示从i到j的这一段树的加分,那么枚举i到j之间的节点做根即可。f[i][j] = max{f[i][k-1] + f[k+1][j] + a[k]}(i<k<j)。对于第二问的输出前序遍历,设tree[i][j]为i到j的根节点,最后输出即可。

Code:

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
//Mystery_Sky
//
#define M 30
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}
int n;
ll f[M][M], tree[M][M], a[M];

inline void print(int l, int r)
{
	if(l > r) return;
	printf("%lld ", tree[l][r]);
	if(l == r) return;
	print(l, tree[l][r] - 1);
	print(tree[l][r] + 1, r);
}

int main() {
	n = read();
	for(int i = 1; i <= n; i++) {
		a[i] = read();
		tree[i][i] = i;
		f[i][i] = a[i];
	}
	for(int len = 1; len <= n; len++) {
		for(int i = 1; i + len <= n; i++) {
			int j = i + len;
			tree[i][j] = i;
			f[i][j] = f[i][i] + f[i+1][j];
			for(int k = i + 1; k < j; k++) {
				if(f[i][k-1] * f[k+1][j] + f[k][k] > f[i][j]) {
					f[i][j] = f[i][k-1] * f[k+1][j] + f[k][k];
					tree[i][j] = k;
				}
			}
		}
	}
	printf("%lld\n", f[1][n]);
	print(1, n);
	return 0;
}

t4.传染病控制

题目

题意:给定一棵树,从根节点开始逐步往下,每次感染一层,每次可以切断树上的一条边,求最少感染人数。

思路:[贪心(错误)]
拿到这道题,首先想到了贪心,就是每次割边的时候割掉与子树最大的点所连的边。然后得了90分。但是事实上这道题目贪心是错误的,能得到90分完全是因为数据过水。
为什么贪心错了呢?举个栗子:如果当前节点有一个左子树和一个右子树,其中左子树是一个很长很长的链,而右子树是一棵比左子树略短的二叉树,那么我们显然应该割掉与右子树相连的边,而贪心却是去割左子树,这样就错了。

Code:

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
//Mystery_Sky
//误人子弟的90分错误贪心
#define M 1000
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}
struct Edge{
	int to, next;
}edge[M];
int n, m, tot, max_dep;
int son[M], dep[M], remain[M], f[M];
int cnt, head[M];

inline void add_edge(int u, int v)
{
	edge[++cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

void dfs(int p, int fa)
{
	if(son[p]) return;
	son[p] = 1;
	for(int i = head[p]; i ; i = edge[i].next) {
		int v = edge[i].to;
		if(v == fa) continue;
		dep[v] = dep[p] + 1;
		max_dep = max(dep[v], max_dep);
		dfs(v, p);
		son[p] += son[v];
		f[v] = p;
	}
}

void del(int p)
{
	remain[p] = 0;
	for(int i = head[p]; i; i = edge[i].next) {
		int v = edge[i].to;
		if(v == f[p]) continue;
		del(v);
	}
	return;
}

void dfs_1(int d)
{
	if(d > max_dep) return;
	int pos = 0, Max = 0; 
	for(int p = 1; p <= n; p++) {
		if(remain[p] && dep[p] == d) {
			for(int i = head[p]; i; i = edge[i].next) {
				int v = edge[i].to;
				if(v == f[p]) continue;
				if(son[v] >= Max) pos = v, Max = son[v];
			}	
		}
	}
	tot += Max;
	del(pos);
	dfs_1(d+1);
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= m; i++) {
		int u = read(), v = read();
		add_edge(u, v);
		add_edge(v, u);
	}
	dep[1] = 1;
	dfs(1, 0);
	for(int i = 1; i <= n; i++) remain[i] = 1;
	dfs_1(1);
	printf("%d\n", n - tot);
	return 0;
}

正解:考完试之后试着往搜索那方面去想了想,然后参考了部分题解,解决了这道题。因为n的范围很小,所以没有必要追求太高的效率,完全可以直接爆搜对吧,连剪枝都可以不用。。。

Code:

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
//Mystery_Sky
//
#define M 400
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

struct Tree{
	int fa, son[M], sons, size;
}tree[M];

int n, m, ans, max_dep;
int dep[M][M], remain[M], f[M];
int cnt, head[M];

void dfs(int p, int d)
{
	//if(!tree[p].sons) return;
	max_dep = max(d, max_dep);
	for(int i = 1; i <= tree[p].sons; i++) {
		dep[d][0]++;
		dep[d][dep[d][0]] = tree[p].son[i];
		dfs(tree[p].son[i], d+1);
	}
	return;
}

int get_size(int p)
{
	for(int i = 1; i <= tree[p].sons; i++) {
		tree[p].size += get_size(tree[p].son[i]);
	}
	return tree[p].size;
}

void del(int p)
{
	remain[p] = 0;
	for(int i = 1; i <= tree[p].sons; i++) {
		int v = tree[p].son[i];
		del(v);
	}
	return;
}

void remark(int p)
{
	remain[p] = 1;
	for(int i = 1; i <= tree[p].sons; i++) {
		int v = tree[p].son[i];
		remark(v);
	}
	return;
}

void dfs_1(int d, int sum)
{
	if(d == max_dep) {
		ans = max(sum, ans);
		return;
	}
	int s = 0;
	for(int i = 1; i <= dep[d][0]; i++) {
		int u = dep[d][i];
		if(!remain[u]) {
			s++;
			continue;
		}
		del(u);
		dfs_1(d+1, sum + tree[u].size);
		remark(u);
	}
	if(s == dep[d][0]) {
		ans = max(ans, sum);
	}
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= n; i++) tree[i].size = 1;
	for(int i = 1; i <= m; i++) {
		int u = read(), v = read();
		if(u > v) swap(u, v);
		tree[u].son[++tree[u].sons] = v;
		tree[v].fa = u;
	}
	dfs(1, 2);
	get_size(1);
	for(int i = 1; i <= n; i++) remain[i] = 1;
	dfs_1(2, 0);
	//for(int i = 1; i <= n; i++) printf("%d\n", tree[i].size); 
	printf("%d\n", n - ans);
	return 0;
}

总分:100+10+100+90=300

posted @ 2019-10-22 17:19  Mystery_Sky  阅读(193)  评论(0编辑  收藏  举报