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\n", last.x, count);
count = 1;
last = top;
}
//printf("->%d %d<-\n", top.x, count);
}
printf("%d %d\n", 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\n", ECC);
return 0;
}
唯愿,青春不辜负梦想,未来星辰闪耀