ICPC North Central NA Contest 2017解题报告

【题库链接】

https://www.jisuanke.com/contest/7331?view=challenges

【A. Stoichiometry】

【题目大意】
化学方程式配平

【解题思路】
大模拟?不太想写

【AC代码】
https://blog.csdn.net/The___Flash/article/details/104598915

【B. Pokemon Go Go】

【题目大意】
有n个点,每个点有精灵,一个精灵可能有多个点,问你捕捉所有精灵走的最短路程是多少

【解题思路】
有两种方法,一种是记忆化搜索,一种是状压dp

记忆化搜索可以A,但是过不了这个数据

20
42 68 a
135 101 b
170 125 c
79 159 d
163 65 e
106 146 f
82 28 g
162 92 h
196 143 i
28 37 j
192 5 k
103 154 l
93 183 m
22 117 n
119 96 o
48 127 p
172 139 q
70 113 r
68 100 s
36 95 t

所以建议还是写状压dp,但是奇怪的是记忆化搜索在测试数据里面跑得比dp快???(黑人问号)

【AC代码】

//记忆化搜索
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int maxn = (1 << 22);
struct Node {
	int x, y;
	string s;
	Node() { x = y = 0; }
	Node(register int mx, register int my) {
		x = mx, y = my;
	}
}a[25];
inline int getdis(register Node& a, register Node& b) {
	return abs(a.x - b.x) + abs(a.y - b.y);
}
int n, cnt;
bool vis[25];
int dis[25][25];
int dp[maxn][25];
map<string, int> mp;
inline int dfs(register int pos, register int num) {
	if (!num) return dis[pos][0];
	register int tmp = 0;
	for (register int i = 1; i <= cnt; ++i) if (vis[i]) tmp |= (1 << i);
	if (dp[tmp][pos]) return dp[tmp][pos];
	register int res = INF;
	for (register int i = 1; i <= n; ++i) {
		if (vis[mp[a[i].s]]) continue;
		vis[mp[a[i].s]] = true;
		res = min(dfs(i, num - 1) + dis[pos][i], res);
		vis[mp[a[i].s]] = false;
	}
	return dp[tmp][pos] = res;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	for (register int i = 1; i <= n; ++i) {
		cin >> a[i].x >> a[i].y >> a[i].s;
		if (!mp[a[i].s]) mp[a[i].s] = ++cnt;
	}
	for (register int i = 0; i < n; ++i) {
		for (register int j = i + 1; j <= n; ++j) {
			dis[i][j] = dis[j][i] = getdis(a[i], a[j]);
		}
	}
	cout << dfs(0, cnt) << endl;
	return 0;
}
/*
状压dp
dp[i][j]表示在二进制下i情况到达点k的最短路程
参考自:https://blog.csdn.net/weixin_43014282/article/details/104582513
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int maxn = (1 << 22);
struct Node {
	int x, y;
	string s;
	inline Node() { x = y = 0; }
	inline Node(register int mx, register int my) {
		x = mx, y = my;
	}
}a[25];
inline int getdis(register Node& a, register Node& b) {
	return abs(a.x - b.x) + abs(a.y - b.y);
}
vector<int> v[25];
map<string, int> mp;
int tot;
int dis[25][25];
int dp[maxn][25];
int ans = INF;
inline void dfs(register int cur, register int cnt) {
	if (cnt == tot + 1) {
		ans = min(ans, dp[cur][0]);
		return;
	}
	register int l = v[cnt].size();
	for (register int i = 0; i < l; ++i) {
		dfs((cur | (1 << v[cnt][i])), cnt + 1);
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	register int n;
	cin >> n;
	for (register int i = 1; i <= n; ++i) {
		cin >> a[i].x >> a[i].y >> a[i].s;
		if (!mp[a[i].s]) mp[a[i].s] = ++tot;
		v[mp[a[i].s]].push_back(i);
	}
	for (register int i = 0; i < n; ++i) {
		for (register int j = i + 1; j <= n; ++j) {
			dis[i][j] = dis[j][i] = getdis(a[i], a[j]);
		}
	}
	memset(dp, INF, sizeof(dp));
	dp[0][0] = 0;
	int num = 1 << (n + 1);
	for (register int i = 0; i < num; ++i) {
		for (register int j = 0; j <= n; ++j) {
			if ((i & (1 << j)) == 0) {
				for (register int k = 0; k <= n; ++k) {
					dp[i | (1 << j)][j] = min(dp[i | (1 << j)][j], dp[i][k] + dis[k][j]);
				}
			}
		}
	}
	dfs(1, 1);
	cout << ans << endl;
	return 0;
}

【C. Urban Design】

【题目大意】
有n条直线将平面分成多个区域,相邻区域名字不同,现在有m次询问,每次给你两个点问你这两点所在区域的名字是否相同

【解题思路】
很简单,就看这两点连线后与多少条直线相交,如果数量为奇数,那么显然名字不同,否则相同

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
struct pt {
	ll x, y;
	pt() {}
	pt(ll xx, ll yy) {
		x = xx, y = yy;
	}
};
struct ln {
	pt s, e;
	ln() {}
}l[10010];
ll mul(ln a) {
    pt s = a.s, e = a.e;
    return e.x * s.y - s.x * e.y;
}
bool judge(ln a, pt b) {
    pt s = a.s, e = a.e;
    ll dx = e.x - s.x, dy = e.y - s.y;
    return dy * b.x + mul(a) - dx * b.y < 0;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int s;
	cin >> s;
	for (int i = 1; i <= s; ++i) {
		int a, b, c, d;
		cin >> a >> b >> c >> d;
		l[i].e = pt(a, b);
		l[i].s = pt(c, d);
	}
	int T;
	cin >> T;
	while (T--) {
		ll a, b, c, d;
		cin >> a >> b >> c >> d;
		ln tmp;
		tmp.e = pt(a, b);
		tmp.s = pt(c, d);
		int sum = 0;
		for (int i = 1; i <= s; ++i) {
			if (judge(l[i], tmp.s) != judge(l[i], tmp.e)) ++sum;
		}
		if (sum & 1) cout << "different" << endl;
		else cout << "same" << endl;
	}
	return 0;
}

【D. Smooth Array】

【题目大意】
给你一个长度为n的数组,每次你可以将数组中的一个数变成0 - s中的任意一个数,问你最少多少次后数组中所有连续的k个数的和为s

【解题思路】
二维dp,dp[i][j]表示前i个数和为j所需要的的最小次数,冉儿饿不会

【AC代码】
https://blog.csdn.net/tianyizhicheng/article/details/104600786

【E. Is-A? Has-A? Who Knowz-A?】

【题目大意】
有以下四种规则:
A is B, B is C => A is C
A has B, B is C => A has C
A is B, B has C => A has C
A has B, B has C => A has C

现在先告诉你n个已经成立的关系,然后让你推断另外m个关系是否正确

注意A is A成立,但是A has A不一定成立

当时WA就WA在这,比较可惜

【解题思路】
Floyd求传递闭包

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
bool has[510][510];
bool is[510][510];
map<string, int> mp;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	register int n, m;
	cin >> n >> m;
	memset(is, 0, sizeof(is));
	memset(has, 0, sizeof(has));
	int cnt = 0;
	for (register int i = 1; i <= n; ++i) {
		register string a, b, c;
		cin >> a >> b >> c;
		if (!mp.count(a)) mp[a] = ++cnt;
		if (!mp.count(c)) mp[c] = ++cnt;
		int x = mp[a], y = mp[c];
		if (b[0] == 'h') {
			has[x][y] = true;
		}
		else {
			is[x][y] = true;
		}
	}
	for (int k = 1; k <= cnt; ++k) {
		for (int i = 1; i <= cnt; ++i) {
			if (i == k) continue;
			for (int j = 1; j <= cnt; ++j) {
				if (j == k || i == j) continue;
				if (is[i][k] && is[k][j]) is[i][j] = true;
			}
		}
	}
	for (int k = 1; k <= cnt; ++k) {
		for (int i = 1; i <= cnt; ++i) {
            if(i == k) continue;
			for (int j = 1; j <= cnt; ++j) {
				if ((has[i][k] && has[k][j]) || (has[i][k] && is[k][j]) || (is[i][k] && has[k][j])) has[i][j] = true;
			}
		}
	}
    for (int i = 0; i <= cnt; ++i) {
		is[i][i] = true;
	}
	register int cas = 0;
	while (m--) {
		register string a, b, c;
		cin >> a >> b >> c;
		bool flag = false;
		if (b[0] == 'h') flag = has[mp[a]][mp[c]];
		else flag = is[mp[a]][mp[c]];
		if (flag) printf("Query %d: true\n", ++cas);
		else printf("Query %d: false\n", ++cas);
	}
	return 0;
}

【F. Atlantis】

【题目大意】
给你n个海上的点,每个点的访问时间为t,高度为h,没过1s海水会上涨1m,问你在所有点全部淹没前你能访问的最大数目是多少

【解题思路】
假设你的最优解为a1,a2,a3…am,那么其一定满足:
t(a1) <= h(a1)
t(a1) + t(a2) <= h(a2)
t(a1) + t(a2) + t(a3) <= h(a3)

所以我们首先需要对所有的点按高度排序,尽量将高度小的点优先访问,然后为了找到一组解,先将解队列置空,然后假设最优解里面存在ai,将ai入队,如果此时我们的总时间T > h(ai),那么我们将队列里面的访问时间最长的点出队,这样队列的最大长度即为最优解

事实上这个解法有点类似于单调队列,不同在于我们这里用到的是优先级队列

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int maxn = 200010;
struct Node {
    int t, h;
    inline bool operator < (const Node& m_n)const {
        if(h == m_n.h) return t < m_n.t;
        return h < m_n.h;
    }
}a[maxn];
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    register int n;
    cin >> n;
    for(register int i = 1; i <= n; ++i) {
        cin >> a[i].t >> a[i].h;
    }
    sort(a + 1, a + n + 1);
    register priority_queue<int> que;
    register ll T = 0, ans = 0;
    for(register int i = 1; i <= n; ++i) {
        que.push(a[i].t);
        T += a[i].t;
        while(!que.empty() && T > a[i].h) {
            T -= que.top();
            que.pop();
        }
        ans = max((ll)que.size(), ans);
    }
    cout << ans << endl;
    return 0;
}

【G. Sheba’s Amoebas】

【题目大意】
给你一个黑白图,问你独立黑块的个数

【解题思路】
签到题,简单dfs,每遇到一个黑块就把与他相连的黑块标记,计算总数目

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int n, m;
char Map[110][110];
bool vis[110][110];
int dx[] = { 0, 0, 1, -1, 1, -1, 1, -1 };
int dy[] = { 1, -1, 0, 0, 1, -1, -1, 1 };
bool flag = false;
inline void dfs(int x, int y, int startx, int starty) {
	if (vis[x][y] && x == startx && y == starty) {
		flag = true;
		return;
	}
	if (vis[x][y]) return;
	vis[x][y] = true;
	bool flag = false;
	for (int i = 0; i < 8; ++i) {
		if (Map[x + dx[i]][y + dy[i]] == '#') {
			dfs(x + dx[i], y + dy[i], startx, starty);
			if (flag) return;
		}
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			cin >> Map[i][j];
		}
	}
	int sum = 0;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (vis[i][j]) continue;
			if (Map[i][j] == '#') {
				flag = false;
				dfs(i, j, i, j);
				sum += flag;
			}
		}
	}
	cout << sum << endl;
	return 0;
}

【H. Zebras and Ocelots】

【题目大意】
有一个由Z和O组成的柱子,每次最下面的O会变成Z并且该O以下的Z会变成O,问你几次之后全部都变成Z

【解题思路】
一开始看数据很小,以为暴力直接过,后来发现暴力的时间是指数级的,其实很简单,把Z看成1,把O看成0,那么每次操作相当于为二进制下加1,并且最后会变成1111111…,做个减法就好了

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
inline ll qpow(ll m, int n) {
	ll res = 1;
	while (n) {
		if (n & 1) res *= m;
		m *= m;
		n >>= 1;
	}
	return res;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	ll sum = 0;
	for (int i = 1; i <= n; ++i) {
		char c;
		cin >> c;
		if (c == 'Z') {
			sum += qpow(2, n - i);
		}
	}
	sum = qpow(2, n) - sum - 1;
	cout << sum << endl;
	return 0;
}

【I. Racing Around the Alphabet】

【题目大意】
有一个圆盘分成28份,每份都有字母,给你一句话,问你走完这句话要多长时间,圆盘直径60m,你的速度15m/s,每次要停顿1s

【解题思路】
纯模拟就行了,但是精度要求有点高

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
#define PI acos(-1)
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	map<char, int> mp;
	for (int i = 0; i < 26; ++i) {
		mp[i + 'A'] = i;
	}
	mp[' '] = 26, mp['\''] = 27;
	int n;
	cin >> n;
	string s = "\n";
	getline(cin, s);
	while (n--) {
		getline(cin, s);
		double sum = 1.0;
		int l = s.length();
		for (int i = 1; i < l; ++i) {
			int t = mp[s[i]], tt = mp[s[i - 1]];
			int tmp = abs(mp[s[i]] - mp[s[i - 1]]);
			sum += min(tmp, 28 - tmp) * PI / 7.0 + 1;
		}
		printf("%.10f\n", sum);
	}
	return 0;
}

【J. Lost Map】

【题目大意】
给出一个图的邻接矩阵,让你输出其最小生成树上的所有边

【解题思路】
MST裸题

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
int G[2510][2510];
bool vis[2510];
int dis[2510];
int pre[2510];
vector<pair<int, int> > v;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			cin >> G[i][j];
		}
	}
	for (int i = 1; i <= n; ++i) {
		pre[i] = 1;
		dis[i] = G[1][i];
	}
	vis[1] = true;
	int sum = 0;
	for (int i = 1; i < n; ++i) {
		int k = -1;
		int Min = INF;
		for (int j = 1; j <= n; ++j) {
			if (!vis[j] && dis[j] < Min) {
				Min = dis[j];
				k = j;
			}
		}
		if (k == -1) break;
		sum += Min;
		vis[k] = true;
		for (int j = 1; j <= n; ++j) {
			if (!vis[j] && dis[j] > G[k][j]) {
				dis[j] = G[k][j];
				pre[j] = k;
			}
			if (pre[k] == j && G[k][j] == Min && Min != 0) {
				v.push_back(make_pair(k, j));
			}
		}
	}
	for (auto& it : v) {
		cout << it.first << " " << it.second << endl;
	}
	return 0;
}
发布了40 篇原创文章 · 获赞 2 · 访问量 3210

猜你喜欢

转载自blog.csdn.net/weixin_44211980/article/details/104602663