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水題

然後就沒有再做啦~~

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