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
唯愿,青春不辜负梦想,未来星辰闪耀