AtCoder ABC277 A~F

https://atcoder.jp/contests/abc277
1500pts(ABCDE), VP.

A - ^{-1}

不讲 - AC

int main() {
	setIO("");
	
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
		if (a[i] == m) {
			cout << i << "\n";
			return 0;
		}
	}
	
	return 0;
}

B - Playing Cards Validation

麻烦的 - AC

在AtCoder上打比赛总是太急了,连条件都没看清楚,交了5次才过。

int n;
string s[MAXN];
set<string> st;
 
int main() {
	setIO("");
	
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		cin >> s[i];
		if (s[i][0] != 'H' && s[i][0] != 'D' && s[i][0] != 'C' && s[i][0] != 'S') {
			cout << "No\n";
			return 0;
		}
		if (s[i][1] == '1' || s[i][1] == '0' || !isdigit(s[i][1]) && s[i][1] != 'A' && s[i][1] != 'T' && s[i][1] != 'J' && s[i][1] != 'Q' && s[i][1] !='K') {
			cout << "No\n";
			return 0;
		}
		if (st.find(s[i]) != st.end()) {
			cout << "No\n";
			return 0;
		}
		st.insert(s[i]);
	}
	cout << "Yes\n";
	
	return 0;
}

更好的 - AC(补)

要判断s[i][1]是否是A23456789TJQK之一,我当时也没多想,直接一个一个打if。
可以用count或string.find函数。
另外,判重时我用了set。实际上,对这题来说,暴力枚举好像更简单一点。

int n;
string s[MAXN];
 
int main() {
	setIO("");
	
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		cin >> s[i];
		if (s1.find(s[i][0]) == s1.npos || s2.find(s[i][1]) == s2.npos) {
			cout << "No\n";
			return 0;
		}
//		if (!count(s1.begin(), s1.end(), s[i][0]) || !count(s2.begin(), s2.end(), s[i][1])) {
//			cout << "No\n";
//			return 0;
//		}
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = i + 1; j <= n; ++j) {
			if (s[i] == s[j]) {
				cout << "No\n";
				return 0;
			}
		}
	}
	cout << "Yes\n";
	
	return 0;
}

C - Ladder Takahashi

并查集 - AC

也考虑过搜索,但是开不出图,因为最多有1e9个点。

int n, ans;
map<int, int> fa;
 
int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
	fa[find(x)] = find(y);
}
 
int main() {
	setIO("");
	
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		int a, b;
		cin >> a >> b;
		if (fa.find(a) == fa.end()) fa[a] = a;
		if (fa.find(b) == fa.end()) fa[b] = b;
		merge(a, b);
	}
	
	ans = 1;
	for (auto it = fa.begin(); it != fa.end(); ++it) {
		int i = it->first;
		if (find(i) == find(1)) {
			ckmax(ans, i);
		}
	}
	cout << ans << "\n";
	
	return 0;
}

STL & BFS - AC(补)

从官方题解里抄下来的代码,对STL的运用值得学习。 看注释吧。

int n;
map<int, vector<int> > g; //完美代替vector<int> g[MAXN]
 
int main() {
	setIO("");
	
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		int a, b; cin >> a >> b;
		g[a].push_back(b); g[b].push_back(a);
	}
	
	queue<int> q; q.push(1);
	set<int> s; s.insert(1); //代替bool vis[MAXN]
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (int v : g[u]) { //遍历vector
			if (s.find(v) == s.end()) {
				q.push(v);
				s.insert(v);
			}
		}
	}
	cout << *s.rbegin() << "\n"; //漂亮地输出答案
	
	return 0;
}

D - Takahashi’s Solitaire

排序 - AC

排个序,注意循环。

int n, m;
ll a[MAXN], tot, ans;
 
int main() {
	setIO("");
	
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
		tot += a[i];
	}
	
	ans = tot;
	sort(a + 1, a + n + 1);
	ll s = a[1];
	for (int i = 2; i <= n; ++i) {
		if (a[i] > a[i - 1] + 1) {
			ckmin(ans, tot - s);
			s = 0;
		}
		s += a[i];
	}
	ckmin(ans, tot - s);
	if ((a[n] + 1) % m == a[1]) {
		for (int i = 1; i <= n; ++i) {
			if (i > 1 && a[i] > a[i - 1] + 1) {
				ckmin(ans, tot - s);
				break;
			}
			s += a[i];
		}
	}
	cout << ans << "\n";
	
	return 0;
}

E - Crystal Switches

拆点 & 双端队列BFS - AC

一开始,我以为按开关也算在步数当中,就写了以下算法(不审题×N):
图有两种状态,可以看成两个图。把每个点都拆成两个。有开关的点,拆出来的两个点之间有边。
在整张图上跑最短路(BFS)。

写完发现不过样例(所以要先手算样例)。
实际上,同一个点,拆出来的两个点之间若有边,权为0。
还好,整张图上边权只有0或1,无需把BFS改成Dijkstra,只需改用双端队列。

int n, m, k, dis[MAXN * 2];
vector<int> g[MAXN * 2];
 
void bfs() {
	memset(dis, 0x3f, sizeof(dis)); dis[1] = 0;
	deque<int> q; q.push_back(1);
	while (!q.empty()) {
		int u = q.front(); q.pop_front();
		// cout << u << "\n";
		for (int i = 0; i < g[u].size(); ++i) {
			int v = g[u][i];
			if (abs(v - u) == n) {
				if (ckmin(dis[v], dis[u])) q.push_front(v);
			}
			else {
				if (ckmin(dis[v], dis[u] + 1)) q.push_back(v);
			}
		}
	}
}
 
int main() {
	setIO("");
	
	cin >> n >> m >> k;
	for (int i = 1; i <= m; ++i) {
		int u, v, a; cin >> u >> v >> a;
		if (a) { g[u].push_back(v); g[v].push_back(u); }
		else  { g[u + n].push_back(v + n); g[v + n].push_back(u + n); }
	}
	for (int i = 1; i <= k; ++i) {
		int s; cin >> s;
		g[s].push_back(s + n); g[s + n].push_back(s);
	}
	
	bfs();
	int ans = min(dis[n], dis[n + n]);
	if (ans < INF) cout << ans << "\n";
	else cout << "-1\n";
	
	return 0;
}

F - Sorting a Matrix

分析

做到这题时,还剩55min.

显然,问题可转化为先做行交换,后做列交换(反过来也行),可用矩阵乘法的结合律证明。
行交换简单。上一行所有数比下一行小。可以先将行按各行max排序,排完序判断上一行max是不是都比下一行min小。如果不是输出No,否则进行列交换。
突然想到,为什么在汉语当中,“上一行”和“下一行”是相邻的两行?
接下来通过列交换,把每一行分别排序。问题在于,列是一起动的。这就难了。

这题我想了近40min,中途用5min看了G题,发现更难。最后只好骗分。

骗分 - WA

共71个点,我过了51个。
可是"Yes"点有34个,"No"点有37个。我是怎么做到的?
随机。XD

如果结合上面的思路,只通过行交换就能判断出一部分"No",再骗分,也许能过更多的点。
但是我没写。

int main() {
	srand(time(0));
	int x = rand();
	if (x & 1) cout << "Yes\n";
	else cout << "No\n";
	
	return 0;
}

拓扑排序 - TLE(赛后)

行交换后。
对于每一行,可以得出某些列之间的前后关系。以列为节点,建一张图,只要没有环就行。
这些比赛时都想到了。但是,如果有两个数相等怎么办?它们之间谁在前,谁在后?

当时想了半天,真是弱智。两个数相等,就得不到这两列的关系
这样,对于每一行排序,在相邻的两个数之间建边即可。当然,如果有两块,1 1 1 1 1 2 2 2 2 2 ,就要每一个1建一条边到每一个2——
导致这部分的时间复杂度是 O ( W 2 ) O(W^2) O(W2),超时。

struct Node {
	int v, ind;
	Node(int _v, int _ind): v(_v), ind(_ind){}
	bool operator < (const Node &x) const {
		return v < x.v;
	}
};
struct Arr {
	vector<Node> a;
	int mx, mn;
	Arr(int _mx = -1, int _mn = INF): mx(_mx), mn(_mn){}
	bool operator < (const Arr &x) const {
		return mx < x.mx;
	}
} a[MAXH];
	
 
int h, w, in[MAXH];
vector<int> g[MAXH];
 
bool toposort() {
	int cnt = 0;
	queue<int> q;
	for (int i = 0; i < w; ++i) {
		if (!in[i]) {
			q.push(i);
			++cnt;
		}
	}
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (int i = 0; i < g[u].size(); ++i) {
			if (!--in[g[u][i]]) {
				q.push(g[u][i]);
				++cnt;
			}
		}
	}
	return cnt >= w;
}
 
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	
	cin >> h >> w;
	for (int i = 0; i < h; ++i) {
		for (int j = 0; j < w; ++j) {
			int tmp; cin >> tmp;
			a[i].a.push_back(Node(tmp, j));
			if (tmp) ckmin(a[i].mn, tmp), ckmax(a[i].mx, tmp);
		}
	}
	
	sort(a, a + h);
	for (int i = 1; i < h; ++i) {
		if (a[i].mn < a[i - 1].mx) {
			cout << "No\n";
			return 0;
		}
	}
	
	for (int i = 0; i < h; ++i) {
		sort(a[i].a.begin(), a[i].a.end());
		int l1, l2;
		for (l1 = 0; l1 < w && !a[i].a[l1].v; ++l1);
		for (l2 = l1 + 1; l2 < w && a[i].a[l2].v == a[i].a[l1].v; ++l2);
		for (int j = l2; j < w; ++j) {
			if (a[i].a[j].v > a[i].a[l2].v) l1 = l2, l2 = j;
			for (int k = l1; k < l2; ++k) g[a[i].a[k].ind].push_back(a[i].a[j].ind);
		}
	}
	
	for (int i = 0; i < w; ++i) {
		for (int j = 0; j < g[i].size(); ++j) {
					cout << g[i][j] << ' ';
			++in[g[i][j]];
		}
	}
	cout << (toposort() ? "Yes\n" : "No\n");
	
	return 0;
}

虚点 & 拓扑排序 - WA(补)

1 1 1 1 1 2 2 2 2 2,超时的解决方法是,
5个1分别建一条边到一个虚点上,虚点建边到五个2上。 妙!
时间复杂度由 O ( W 2 ) O(W^2) O(W2)降为 O ( W ) O(W) O(W)

不知道哪里写错了,WA 7个点。

struct Node {
	int v, ind;
	Node(int _v, int _ind): v(_v), ind(_ind){}
	bool operator < (const Node &x) const {
		return v < x.v;
	}
};
struct Arr {
	vector<Node> a;
	int mx, mn;
	Arr(int _mx = -1, int _mn = INF): mx(_mx), mn(_mn){}
	bool operator < (const Arr &x) const {
		return mx < x.mx;
	}
} a[MAXH];
	
 
int h, w, in[MAXH * 3];
vector<int> g[MAXH * 3];
 
bool toposort() {
	int cnt = 0;
	queue<int> q;
	for (int i = 0; i <= w + h * w; ++i) {
		if (!in[i]) {
			q.push(i);
			++cnt;
		}
	}
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (int i = 0; i < g[u].size(); ++i) {
			if (!--in[g[u][i]]) {
				q.push(g[u][i]);
				++cnt;
			}
		}
	}
	return cnt >= w + h * w + 1;
}
 
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	
	cin >> h >> w;
	for (int i = 0; i < h; ++i) {
		for (int j = 0; j < w; ++j) {
			int tmp; cin >> tmp;
			a[i].a.push_back(Node(tmp, j));
			if (tmp) ckmin(a[i].mn, tmp), ckmax(a[i].mx, tmp);
		}
	}
	
	sort(a, a + h);
	for (int i = 1; i < h; ++i) {
		if (a[i].mn < a[i - 1].mx) {
			cout << "No\n";
			return 0;
		}
	}
	
	for (int i = 0; i < h; ++i) {
		sort(a[i].a.begin(), a[i].a.end());
		for (int j = 1; j < w; ++j) {
			if (!a[i].a[j - 1].v) continue;
			if (a[i].a[j].v != a[i].a[j - 1].v) {
				int v = a[i].a[j - 1].v;
				for (int k = j - 1; k >= 0 && a[i].a[k].v == v; --k) {
					g[a[i].a[k].ind].push_back(v + w);
				}
				for (int k = j; k < w && a[i].a[k].v == a[i].a[j].v; ++k) {
					g[v + w].push_back(a[i].a[k].ind);
				}
			}
		}
	}
	
	for (int i = 0; i <= w + h * w; ++i) {
		for (int j = 0; j < g[i].size(); ++j) {
			++in[g[i][j]];
		}
	}
	cout << (toposort() ? "Yes\n" : "No\n");
	
	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值