Noip2007提高组

2水题 + 1中等 + 1稍难


t1.统计数字

题目

题意:给定n个数,按数字从小到大的顺序输出数字及出现次数。

思路:[水题]爱怎么搞怎么搞,反正我就是要用优先队列。

Code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
//Mystery_Sky
//
#define INF 0x3f3f3f3f
#define M 1000100
#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;
struct node{
	int x;
	inline bool operator <(const node& a) const{
		return a.x < x;
	}
};
priority_queue <node> Q;

int main() {
	n = read();
	for(int i = 1; i <= n; i++) {
		int x = read();
		Q.push((node){x}); 
	}
	node last = Q.top();
	Q.pop();
	int count = 1;
	while(!Q.empty()) {
		node top = Q.top();
		Q.pop();
		if(top.x == last.x) count++;
		else {
			printf("%d %d
", last.x, count);
			count = 1;
			last = top;
		}
		//printf("->%d %d<-
", top.x, count);
	}
	printf("%d %d
", last.x, count);
	return 0;
}

t2.字符串的展开

题目

题意:给定三个参数p1,p2,p3,按相应的要求展开给定的字符串。

思路:[模拟]要你干嘛就照着做不就过了。

Code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <string>
using namespace std;
//Mystery_Sky
//
#define INF 0x3f3f3f3f
#define M 1000100
#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 p1, p2, p3;
string st, ans;
int pos[M];

inline void give_number(char a, char b)
{
	if(p1 == 3) {
		for(int i = 1; i <= b-a-1; i++) {
			for(int j = 1; j <= p2; j++) ans += '*';
		}
	}
	else {
		if(p3 == 1) {
			for(char i = a+1; i < b; i++) {
				for(int j = 1; j <= p2; j++) ans += i;
			}
		}
		else {
			for(char i = b-1; i > a; i--) {
				for(int j = 1; j <= p2; j++) ans += i;
			}
		}
	}
	return;
}

inline void give_word(char a, char b)
{
	if(p1 == 3) {
		for(int i = 1; i <= b-a-1; i++) 
			for(int j = 1; j <= p2; j++) ans += '*';
	}
	else if(p1 == 1) {
		if(p3 == 1) {
			for(char i = a+1; i < b; i++)
				for(int j = 1; j <= p2; j++) ans += i;
		}
		else {
			for(char i = b-1; i > a; i--)
				for(int j = 1; j <= p2; j++) ans += i;
		}
	}
	else {
		if(p3 == 1) {
			for(char i = a+1; i < b; i++)
				for(int j = 1; j <= p2; j++) ans += (i-32);
		}
		else {
			for(char i = b-1; i > a; i--)
				for(int j = 1; j <= p2; j++) ans += (i-32);
		}
	}
}

int main() {
	p1 = read(), p2 = read(), p3 = read();
	cin >> st;
	int len = st.length();
	for(int i = 0; i < len; i++) {
		if(st[i] != '-') ans += st[i];
		else {
			if(st[i+1] >= '0' && st[i+1] <= '9' && st[i-1] >= '0' && st[i-1] <= '9') {
				if(st[i+1] - st[i-1] == 1) continue;
				else if(st[i+1] - st[i-1] <= 0) ans += st[i];
				else {
					give_number(st[i-1], st[i+1]);
				}
			}
			else if(st[i+1] >= 'a' && st[i+1] <= 'z' && st[i-1] >= 'a' && st[i-1] <= 'z') {
				if(st[i+1] - st[i-1] == 1) continue;
				else if(st[i+1] - st[i-1] <= 0) ans += st[i];
				else {
					give_word(st[i-1], st[i+1]);
				}
			}
			else ans += st[i];
		}
	}
	cout << ans << endl;
	return 0;
}

太菜了,打了近百行


t3.矩阵取数游戏

题目

题意:一个n*m的矩阵,每次可以取走每一行的行首或者行末的一个元素, 每行取数的得分 = 被取走的元素值 * 2的已取元素次方(自行看题理解吧)

思路:[区间dp] 早年凭借自己写出的若干道(<=10道)dp题目。正儿八经地题解我估计会另开一篇写,这里就简单口胡一下:开三维数组,分行处理这个矩阵,每行就是一个简单的区间dp。然后这样你就可以得到60分的高分。剩下40分就靠自己的高精呢(我才不会高精呢,试试__int128吧)

Code:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
//Mystery_Sky
//
#define M 10000100
#define INF 0x3f3f3f3f
#define ll __int128
inline int read()
{
	int x=0;
	int 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;
}
ll f[81][81][81];
int n, m;
int a[81][81];
ll Pow[81];
ll ans;

void print(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x > 9) print(x/10);
    putchar(x%10 + '0');
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++) a[i][j] = read();
	Pow[0] = 1;
	for(int i = 1; i <= m; i++) Pow[i] = Pow[i-1] * 2;
	for(int k = 1; k <= n; k++) {
		ll maxx = 0;
		for(int len = m; len >= 1; len--) {
			for(int i = 1; i + len - 1 <= m; i++) {
				int j = i + len - 1;
				int w = m - (j - i + 1);
				if(i - 1 >= 1) f[i][j][k] = max(f[i][j][k], f[i-1][j][k] + a[k][i-1] * Pow[w]);
				if(j + 1 <= m) f[i][j][k] = max(f[i][j][k], f[i][j+1][k] + a[k][j+1] * Pow[w]);
			}
		}
		for(int i = 1; i <= m; i++) maxx = max(maxx, f[i][i][k] + a[k][i] * Pow[m]);
		ans += maxx;
	}
	print(ans);
	return 0;
}

还是很菜,lzx大佬60分做法只用了18行。


t4.树网的核

题目

题意:如果你觉得这道题题意很繁琐,或者定义过于繁杂,可以看看这个->消防

思路:不会,太复杂了,我太菜了,暴力都打不出,不然就有300分了


总分:100 + 100 + 60 + 0.

(update)t4.树网的核

正解:(参考了《算法竞赛进阶指南》)
树的直径的求法:任取一点,找到树上距离该点最远的结点p,再以p为起点找到距离p最远的结点q,p->q的路径即为树的一条直径。(证明略
最小偏心距的求法:在直径上枚举长度不超过s的路径,暴力计算即可。
优化:根据贪心的思想,求最小偏心距的时候,对于每个起点p,只需找到距离其最远的距离不超过s的点q即可。
Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
//Mystery_Sky
//
#define INF 0x3f3f3f3f
#define ll long long
#define M 500
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 next, to, w;
}edge[5050];

struct node{
    int dis, pos;
};

int n, s, dis_2[M], fa[M], vis[M], dis[M][M];
int head[M], cnt;
vector <int> d;

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;
}

void dfs_2(int p, int f)
{
    fa[p] = f;
    for(int i = head[p]; i; i = edge[i].next) {
        int y = edge[i].to;
        if(y == f) continue;
        dis_2[y] = dis_2[p] + edge[i].w;
        dfs_2(y, p);
    }
}

inline void mark(int l, int r)
{
    while(l != r) {
        d.push_back(l); 
        vis[l] = 1;
        l = fa[l];
    }
    vis[r] = 1;
    d.push_back(r); 
    return;
}

inline void clear_mark(int l, int r)
{
    d.clear();
    while(l != r) {
        vis[l] = 0;
        l = fa[l];
    }
    vis[r] = 0;
    return;
}

inline int get_right(int p)
{
    int pp = p;
    for(;;) {
        if(dis_2[p] - dis_2[fa[pp]] < s) pp = fa[pp];
        else if(dis_2[p] - dis_2[fa[pp]] == s) return fa[pp];
        else return pp;
    }
}

inline void floyd()
{
    for(int i = 1; i <= n; i++) dis[i][i] = 0;
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                if(dis[i][j] > dis[i][k] + dis[k][j]) 
                    dis[i][j] = dis[i][k] + dis[k][j];
}

int main() {
    n = read(), s = read();
    memset(dis, INF, sizeof(dis));
    for(int i = 1; i <= n-1; i++) {
        int u = read(), v = read(), w = read();
        //if(u > v) swap(u, v);
        add_edge(u, v, w);
        add_edge(v, u, w);
        dis[u][v] = dis[v][u] = w;
    }
    floyd();
    node x;
    x.dis = 0;
    for(int i = 1; i <= n; i++) if(dis[1][i] > x.dis) x.dis = dis[1][i], x.pos = i;
    dfs_2(x.pos, 0);
    node y;
    y.dis = 0;
    for(int i = 1; i <= n; i++) if(dis_2[i] > y.dis) y.dis = dis_2[i], y.pos = i;//求出树直径的另一端点。
    dis_2[0] = -INF;    
    int l = y.pos;
    int ECC = INF;
    for(;;) {
        int r = get_right(l);
        mark(l, r);
        int len = d.size();
        int maxx = -1;
        for(int i = 1; i <= n; i++) {
            if(vis[i]) continue;
            else {
                int D = INF;
                for(int j = 0; j < len; j++) {
                    D = min(D, dis[i][d[j]]);
                }
                maxx = max(D, maxx); 
            }
        }
        ECC = min(maxx, ECC);
        if(r == x.pos) break;
        clear_mark(l, r);
        l = fa[l];
    }
    printf("%d
", ECC);
    return 0;
}
原文地址:https://www.cnblogs.com/Benjamin-cpp/p/11745332.html