2017CCCC天梯赛决赛 赛前训练

哎呀~

今年初赛做得太仓促,又犯蠢卡题了,做得不好。

所以现在决定临时训练一下!


下面是今年大区赛的题目信息——



前面什么排序for for for啦,基本stl 搞搞啦,字符串处理啦~

好好读题,想清楚怎么样最好写,最快写!

我发现,多关键字最短路、最小字典序,这些东西CCCC真的好喜欢出啊>.<

也许练一下会有不错的提高哦~


这里贴出几题代码——


【2017年团体程序设计天梯赛-大区赛 2-2】【多项式除法 STL-map】多项式A除以B

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
map<int, double>a, b, ans;
map<int, double>::iterator it;
void print(map<int, double> &ans)
{
	for (it = ans.begin(); it != ans.end(); )
	{
		if (fabs(it->second) < 0.05)ans.erase(it++);
		else it++;
	}

	if (ans.size() == 0)
	{
		puts("0 0 0.0");
	}
	else
	{
		printf("%d", ans.size());
		for (it = --ans.end(); ; --it)
		{
			printf(" %d %.1f", it->first, it->second);
			if (it == ans.begin())break;
		}puts("");
	}
}
int main()
{
	while(~scanf("%d", &n))
	{
		a.clear();
		for (int i = 1; i <= n; ++i)
		{
			int index; double coeff;
			scanf("%d%lf", &index, &coeff);
			a[index] += coeff;
		}
		scanf("%d", &m);
		b.clear();
		for (int i = 1; i <= m; ++i)
		{
			int index; double coeff;
			scanf("%d%lf", &index, &coeff);
			b[index] += coeff;
		}
		
		ans.clear();
		while (a.size() && b.size())
		{
			auto A = --a.end();
			auto B = --b.end();

			double Index = A->first - B->first; if (Index < 0)break;
			double k = A->second / B->second;
			for (auto it : b)
			{
				int index = it.first + Index; double coeff = it.second * k;
				a[index] -= coeff;
			}
			for (it = a.begin(); it != a.end(); )
			{
				if (fabs(it->second) < 0.0001)a.erase(it++);
				else it++;
			}
			ans[Index] += k;
		}
		print(ans);
		print(a);
	}
	return 0;
}
/*
【trick&&吐槽】
<1> 对于这种离散型的数据,也许最好的方法是使用map等结构
<2> 卡精度、卡精度、卡精度、卡精度、卡精度、卡精度

【题意】
实现多项式除法

【分析】
直接从高项开始,向低项方向处理,一项项处理即可。

*/


【2017年团体程序设计天梯赛-大区赛 3-2】【多关键字最短路】周游世界

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 10010, M = 100 * 100 * 200 * 2, Z = 1e9 + 7, inf = 0x3f3f3f3f;
//最多点数100 * 100
//最多边数100 * 100 * 100 * 2
typedef pair<int, int>P;
struct Node
{
	int x, d1, d2;
	bool operator < (const Node & b)const
	{
		if (d1 != b.d1)return d1 > b.d1;
		return d2 > b.d2;
	}
};
priority_queue<Node>q;
int casenum, casei;
int num;
int first[N], id;
int w[M], nxt[M], c_d1[M], c_d2[M], c_c[M];
int a[N];
int D1[N], D2[N], C[N], pre[N];

int ST, ED;
void print(int x)
{
	if (x == ST)return;
	int y = pre[x];
	print(y);
	printf("Go by the line of company #%d from %04d to %04d.\n", C[x], y, x);
}

void ins(int x, int y, int d1, int d2, int c)
{
	w[++id] = y;
	nxt[id] = first[x];
	c_d1[id] = d1;
	c_d2[id] = d2;
	c_c[id] = c;
	first[x] = id;
}
void inq(int x, int d1, int d2, int c, int y)
{
	if (d1 < D1[x] || d1 == D1[x] && d2 < D2[x])
	{
		D1[x] = d1;
		D2[x] = d2;
		C[x] = c;
		pre[x] = y;
		q.push({ x,D1[x], D2[x] });
	}
}
bool e[N];
void dijkstra()
{
	for (int i = 0; i <= 10000; ++i)
	{
		e[i] = 0;
		D1[i] = inf;
	}
	while (!q.empty())q.pop();
	inq(ST, 0, 0, 0, 0);
	while (!q.empty())
	{
		int x = q.top().x;
		int d1 = q.top().d1;
		int d2 = q.top().d2;
		q.pop(); if (e[x])continue;

		if (x == ED)
		{
			printf("%d\n", D1[x]);
			print(ED);
			return;
		}

		e[x] = 1;
		for (int z = first[x]; z; z = nxt[z])
		{
			inq(w[z], D1[x] + c_d1[z], D2[x] + c_d2[z], c_c[z], x);
		}
	}
	puts("Sorry, no line is available.");
}
int main()
{
	id = 1;
	scanf("%d", &num);
	for (int I = 1; I <= num; ++I)
	{
		int g; scanf("%d", &g);
		for (int i = 1; i <= g; ++i)
		{
			scanf("%d", &a[i]);
			if (i != 1)a[i + g - 1] = a[i];
		}
		int top = a[1] == a[g] ? g + g - 1 : g;
		for (int i = 1; i <= g; ++i)
		{
			for (int j = i + 1; j <= top; ++j)
			{
				int x = a[i];
				int y = a[j];
				ins(x, y, j - i, 1, I);
				ins(y, x, j - i, 1, I);
			}
		}
	}
	int q; scanf("%d", &q);
	while (q--)
	{
		scanf("%d%d", &ST, &ED);
		dijkstra();
	}
	return 0;
}
/*
【trick&&吐槽】
这题给我最大的教训是——
做题要冷静,先认真读题,理清题目要你做什么

【题意】
有n(100)个联盟公司。
每个公司有m(100)个停经站。
相邻的停经站之间的的边只属于这一家公司(也就是没有重边)。

K(10)次询问。我们想要使得——
<1> 中途经站最少
<2> 换成次数最少
<3> 记录路线

【分析】
这显然是个最短路,最多会有100 * 100个点,于是这里就应该放弃floyd.
然后我们就最好用dijkstra了。
我们需要记录一个二维最短路,同时还要记一个前驱

【时间复杂度&&优化】


*/



【2017年团体程序设计天梯赛-大区赛 3-3】【状压DP + 贪心】球队“食物链”

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 24, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
char s[N][N];
bool win[N][N];
int n;
int f[1 << 20][20];
int nxt[1 << 20][20];
void print(int sta, int fst)
{
	if (fst == 0)return;
	printf(" %d", fst + 1);
	print(sta ^ 1 << fst, nxt[sta][fst]);
}
void solve()
{
	MS(f, 0); f[1][0] = 1;
	MS(nxt, 63);
	int top = (1 << n) - 1;
	for (int sta = 0; sta <= top; ++sta)
	{
		for (int i = 0; i < n; ++i)if ((sta >> i & 1) && f[sta][i])
		{
			for (int j = 0; j < n; ++j)if ((~sta >> j & 1) && win[j][i])
			{
				f[sta | 1 << j][j] = 1;
				gmin(nxt[sta | 1 << j][j], i);
			}
		}
	}

	for (int i = 1; i < n; ++i)
	{
		if (f[top][i] && win[0][i])
		{
			printf("1");
			print(top, i);
			puts("");
			return;
		}
	}
	puts("No Solution");
}
int main()
{
	while (~scanf("%d", &n))
	{
		for (int i = 0; i < n; ++i)scanf("%s", s[i]);
		for (int i = 0; i < n; ++i)
		{
			for (int j = 0; j < n; ++j)
			{
				win[i][j] = (s[i][j] == 'W' || s[j][i] == 'L');
			}
		}
		solve();
	}
	return 0;
}
/*
【trick&&吐槽】
天梯赛的题目会很喜欢给trick
所以说仔细读题真的是太重要了——
联赛采用主客场双循环赛制,参赛球队两两之间在双方主场各赛一场。
联赛战罢,结果已经尘埃落定。此时,联赛主席突发奇想,希望从中找出一条包含所有球队的“食物链”,来说明联赛的精彩程度。
“食物链”为一个1至N的排列{ T1 T2 ... TN },满足:球队T1战胜过球队T2,球队T2战胜过球队T3,……,球队T(N-1)战胜过球队TN,球队TN战胜过球队T1。
这里的"战胜过"的条件非常重要。

【题意】
我们希望找一个最小字典序的哈密顿环

【分析】
哈密顿环可以按照O(2^n * n * n)的复杂度做DP。
而现在要求字典序最小。
于是显然可以——
把第一个位置和最后一个位置都固定为1
然后按照贪心原则,每次把一个数放在前面做DP

【时间复杂度&&优化】
O(nlogn)

*/


【2016年团体程序设计天梯赛 L3-001】【揹包 贪心】凑零钱

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e4 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
int a[N];
bool f[10005][102];
int pre[10005][102];
void print(int n, int m)
{
	if (pre[n][m] == 0)printf("%d\n", m - pre[n][m]);
	else
	{
		if(pre[n][m] != m)printf("%d ", m - pre[n][m]);
		print(n - 1, pre[n][m]);
	}
}
int main()
{
	while(~scanf("%d%d", &n, &m))
	{
		for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
		sort(a + 1, a + n + 1);
		reverse(a + 1, a + n + 1);
		MS(f[0], 0); f[0][0] = 1;
		for (int i = 1; i <= n; ++i)
		{
			int x = a[i];
			for (int j = m; j >= 0; --j)
			{
				f[i][j] = f[i - 1][j];
				pre[i][j] = j;
			}
			for (int j = m; j >= x; --j)if (f[i - 1][j - x])
			{
				f[i][j] = 1;
				pre[i][j] = j - x;
			}
		}
		if (!f[n][m])puts("No Solution");
		else print(n, m);
	}
	return 0;
}
/*
【题意】
n个数中任选若干个构成和为m
输出字典序最小的解

【分析】
字典序最小还是从大到小向前贪心得稳妥!

*/


【2016年团体程序设计天梯赛 L3-002】【堆栈 双SET查中位数】堆栈

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
char op[100];
multiset<int>a, b;
#include<stack>
stack<int>sta;
void update()
{
	while (a.size() > b.size() + 1)
	{
		int val = *--a.end();
		a.erase(--a.end());
		b.insert(val);
	}
	while (a.size() < b.size())
	{
		int val = *b.begin();
		b.erase(b.begin());
		a.insert(val);
	}
}
int main()
{
	while(~scanf("%d", &n))
	{
		a.clear(); b.clear();
		while (!sta.empty())sta.pop();
		for (int i = 1; i <= n; ++i)
		{
			scanf("%s", op);
			if (!strcmp(op, "Pop"))
			{
				if (sta.empty())puts("Invalid");
				else
				{
					int val = sta.top(); sta.pop();
					if (a.find(val) != a.end())a.erase(a.find(val));
					else if (b.find(val) != b.end())b.erase(b.find(val));
					update();
					printf("%d\n", val);
				}
			}
			else if (!strcmp(op, "PeekMedian"))
			{
				if (a.size())printf("%d\n", *--a.end());
				else puts("Invalid");
			}
			else
			{
				int val; scanf("%d", &val);
				sta.push(val);
				if (a.empty())a.insert(val);
				else if (val <= *--a.end())a.insert(val);
				else b.insert(val);
				update();
			}
		}
	}
	return 0;
}
/*
【trick&&吐槽】
<1> 双set维护中位数时,插入操作是要先做判定的
<2> 如果看代码没找到错,就应该思考算法逻辑的问题了

【题意】
维护栈的同时维护中位数

*/


【2016年团体程序设计天梯赛 L3-003】【并查集】社交集群

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1010, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
int f[N], d[N];
int find(int x)
{
	return f[x] == x ? x : f[x] = find(f[x]);
}
int main()
{
	while(~scanf("%d", &n))
	{
		for (int i = 1; i <= 1000; ++i)
		{
			f[i] = i; d[i] = 0;
		}
		for (int i = 1; i <= n; ++i)
		{
			int g; scanf("%d%*c", &g);
			if (!g)continue; --g;
			int x; scanf("%d", &x);
			int fa = find(x); ++d[fa];
			while (g--)
			{
				int x; scanf("%d", &x);
				x = find(x); 
				if (x != fa)
				{
					f[x] = fa; d[fa] += d[x];
				}
			}
		}
		vector<int>vt; vt.clear();
		for (int i = 1; i <= 1000; ++i)if (find(i) == i && d[i])
		{
			vt.push_back(d[i]);
		}
		sort(vt.begin(), vt.end());
		printf("%d\n", vt.size());
		for (int i = vt.size() - 1; ~i; --i)
		{
			printf("%d%c", vt[i], i == 0 ? '\n' : ' ');
		}
	}
	return 0;
}
/*
【trick&&吐槽】
理解题意非常重要。

【题意】
每个人可能有多个兴趣
如果存在任意一个兴趣相同,则这两个人可以建立朋友关系
问你有多少个极大的朋友关系连通子图

【分析】
直接并查集搞搞就好啦
注意输出的时候是对于连通块的根做输出

*/


至于后面的几道题,我们也看一下——


L3-004肿瘤诊所 简单的BFS

L3-005 垃圾箱分布 无脑最短路

L3-006 迎风一刀斩,耐心细心分类讨论

L3-007 天梯地图 我的天,debug了一天,发现是priority_queue的运算符搞反了,于是反一反之后就得到了一个dijkstra的板子

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 505, M = N * N * 2, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
struct Up
{
	LL c1, c2;
	bool operator < (const Up &b)const
	{
		if (c1 != b.c1)return c1 < b.c1;
		return c2 < b.c2;
	}
	Up operator + (const Up &b)const
	{
		return{ c1 + b.c1, c2 + b.c2 };
	}
};
struct Node
{
	int x; Up up; 
	bool operator < (const Node &b)const
	{
		return b.up < up;//Be Careful
	}
};
struct Dijkstra
{
	int ST, ED;
	int first[N], id;
	int w[M], nxt[M]; Up c[M];
	Up f[N];
	int pre[N];
	bool sure[N];
	priority_queue<Node>q;
	void init()
	{
		for (int i = n + 1; i >= 0; --i)first[i] = 0;
		id = 1;
	}
	void ins(int x, int y, Up up)
	{
		w[++id] = y;
		nxt[id] = first[x];
		c[id] = up;
		first[x] = id;
	}
	void inq(int x, int y, Up up)
	{
		if (up < f[y])
		{
			f[y] = up;
			pre[y] = x;
			q.push({ y,f[y] });
		}
	}
	void dijkstra(int st_, int ed_)
	{
		ST = st_; ED = ed_;
		for (int i = n + 1; i >= 0; --i)
		{
			f[i] = { (LL)1e18, (LL)1e18 };
			sure[i] = 0;
		}
		while (!q.empty())q.pop();
		inq(0, ST, { 0,0 });
		while (!q.empty())
		{
			int x = q.top().x; q.pop();
			if (sure[x])continue; sure[x] = 1;
			for (int z = first[x]; z; z = nxt[z])
			{
				inq(x, w[z], f[x] + c[z]);
			}
		}
	}
	void print(int x)
	{
		if (x == ST)
		{
			printf("%d", ST);
			return;
		}
		print(pre[x]);
		printf(" => %d", x);
	}
	vector<int>rd;
	void getroad(int x)
	{
		if (x == ST)rd.clear();
		else getroad(pre[x]);
		rd.push_back(x);
	}
}d1, d2;
int main()
{
	while (~scanf("%d%d", &n, &m))
	{
		d1.init(); d2.init();
		for (int i = 1; i <= m; ++i)
		{
			int x, y, oneway, len, time;
			scanf("%d%d%d%d%d", &x, &y, &oneway, &len, &time);
			d1.ins(x, y, { time, len });
			d2.ins(x, y, { len, 1 });
			if (!oneway)
			{
				d1.ins(y, x, { time,len });
				d2.ins(y, x, { len,1 });
			}
		}
		int st, ed; scanf("%d%d", &st, &ed);
		d1.dijkstra(st, ed);
		d2.dijkstra(st, ed);
		
		d1.getroad(ed);
		d2.getroad(ed);

		if (d1.rd == d2.rd)
		{
			printf("Time = %d; ", d1.f[ed].c1);
			printf("Distance = %d: ", d1.f[ed].c2);
			d1.print(ed); puts("");
		}
		else
		{
			printf("Time = %d: ", d1.f[ed].c1);
			d1.print(ed); puts("");
			printf("Distance = %d: ", d2.f[ed].c1);
			d2.print(ed); puts("");
		}
	}
	return 0;
}
/*
【题意】
直接的多关键字最短路
使用dijkstra的模板写起来更方便哦~

*/

L3-008 喊山 BFS水题

然后就没有再做啦~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章