【题目链接】
A - 石油采集
题意:有一个$01$矩阵,每次可以拿走两个相邻的$1$,问最多能操作几次。
这题和HDU 1507一样。二维矩阵四连通图是一个二分图,题目的操作事实上就是求这个二分图的最大匹配。
B - 道路建设
最小生成树
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int f[maxn];
struct Edge {
int u, v, cost;
}s[maxn];
bool cmp(Edge &a, Edge &b) {
return a.cost < b.cost;
}
int Find(int x ){
if(x != f[x]) f[x] = Find(f[x]);
return f[x];
}
int main() {
int c, n, m;
while(~scanf("%d%d%d", &c, &n, &m)) {
for(int i = 1; i <= m; i ++) {
f[i] = i;
}
for(int i = 1; i <= n; i ++) {
scanf("%d%d%d", &s[i].u, &s[i].v, &s[i].cost);
}
sort(s + 1, s+1+n, cmp);
for(int i =1; i <= n; i ++) {
int fu = Find(s[i].u);
int fv = Find(s[i].v);
if(fu == fv) continue;
f[fu] = fv;
c -= s[i].cost;
if(c < 0) break;
}
if(c < 0) printf("No\n");
else printf("Yes\n");
}
return 0;
}
C - 求交集
类似于归并排序那样搞就可以了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int a[maxn], b[maxn], ans[maxn];
int n, m, sz;
int main() {
while(~scanf("%d%d", &n, &m)) {
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= m; i ++) scanf("%d", &b[i]);
int p1 = 1, p2 = 1;
sz = 0;
while(p1 <= n && p2 <= m) {
if(a[p1] == b[p2]) {
ans[sz ++] = a[p1];
p1 ++;
p2 ++;
} else {
if(a[p1] > b[p2]) p2 ++;
else p1 ++;
}
}
if(sz == 0) {
printf("empty\n");
continue;
}
for(int i = 0; i < sz; i ++) {
printf("%d", ans[i]);
if(i < sz - 1) printf(" ");
else printf("\n");
}
}
return 0;
}
D - 小明的挖矿之旅
由于只能向右或者向下走,所以变成了一张有向无环图。我们只要比较出度为$0$的点的个数和入度为$0$的点的个数即可。
但要注意几种特殊情况:全是孤立点、没有点。
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x7FFFFFFF;
const int maxn = 1100;
char s[maxn][maxn];
int in[maxn * maxn];
int ou[maxn * maxn];
int n, m;
int out(int x, int y) {
if(x < 0 || x >= n) return 1;
if(y < 0 || y >= m) return 1;
return 0;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
for(int i = 0; i < n; i ++) {
scanf("%s", s[i]);
}
memset(in, 0, sizeof in);
memset(ou, 0, sizeof ou);
for(int i = 0; i < n; i ++) {
for(int j = 0; j < m; j ++) {
if(s[i][j] == '#') continue;
if(out(i, j + 1) == 0 && s[i][j + 1] != '#') {
in[i * m + j + 1] ++;
ou[i * m + j] ++;
}
if(out(i + 1, j) == 0 && s[i + 1][j] != '#') {
in[(i + 1) * m + j] ++;
ou[i * m + j] ++;
}
}
}
int sum1 = 0, sum2 = 0, sum3 = 0;
for(int i = 0; i < n; i ++) {
for(int j = 0; j < m; j ++) {
if(s[i][j] == '#') continue;
if(in[i * m + j] == 0) sum1 ++;
if(ou[i * m + j] == 0) sum2 ++;
sum3 ++;
}
}
if(sum3 == 0) {
printf("%d\n", 0);
return 0;
}
if(sum1 == sum3 && sum2 == sum3) {
printf("%d\n", sum1 - 1);
}
else
printf("%d\n", max(sum1, sum2));
}
return 0;
}
E - 通知小弟
先对图进行强连通分量缩点,因为一个强连通分量内部一旦有一个人得到通知,所有人都可以得到通知。
缩点后形成了一张有向无环图,我们只要通知到新图中那些入度为$0$的点,所有的人都能得到通知。
对于无解的情况,我们只要检查HA能通知到的人是否cover了新图中所有入度为$0$的点。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 550;
int n, m;
int a[maxn];
int ans[maxn];
int in[maxn];
#define MAXN 550
struct Node {
int v;
int next;
} edge[500010];
int sz;
int head[MAXN];
int dfn[MAXN];
int low[MAXN];
bool mark[MAXN]; //to judge in stack or not
int id[MAXN]; //the id of scc
int T;
stack<int> sta;
int scc;
void tarjan(int v) {
dfn[v] = low[v] = ++T;
sta.push(v);
mark[v] = true;
int k;
for (k = head[v]; k != -1; k = edge[k].next) {
int u = edge[k].v;
if (!dfn[u]) {
tarjan(u);
low[v] = min(low[v], low[u]);
} else if (mark[u])
low[v] = min(low[v], dfn[u]);
}
if (low[v] == dfn[v]) {
++scc;
int u;
do {
u = sta.top();
sta.pop();
mark[u] = false;
id[u] = scc;
} while (u != v);
}
return;
}
void solve(int n) {
scc = 0;
int i;
for (i = 1; i <= n; ++i)
if (!dfn[i])
tarjan(i);
memset(mark, false, sizeof (mark));
int k;
for (i = 1; i <= n; ++i)
for (k = head[i]; k != -1; k = edge[k].next)
if (id[i] != id[edge[k].v])
mark[id[i]] = true;
return ;
for(int i = 1; i <= n; i ++) {
printf("%d : %d\n", i, id[i]);
}
int p;
int cnt(0);
for (i = 1; i <= scc; ++i) {
if (!mark[i]) {
++cnt;
p = i;
}
}
if (cnt > 1)
printf("0\n");
else {
cnt = 0;
for (i = 1; i <= n; ++i)
if (id[i] == p) ++cnt;
printf("%d\n", cnt);
}
}
int main() {
while(~scanf("%d%d", &n, &m)) {
for(int i = 1; i <= m; i ++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; ++i) {
head[i] = -1;
dfn[i] = 0;
mark[i] = false;
}
sz = 0;
for (int i = 1; i <= n; ++i) {
int num;
scanf("%d", &num);
while(num --) {
int to;
scanf("%d", &to);
edge[sz].v = to;
edge[sz].next = head[i];
head[i] = sz ++;
}
}
solve(n);
memset(in, 0, sizeof in);
memset(ans, 0, sizeof ans);
for(int i = 1; i <= n; i ++) {
for(int j = head[i]; j != -1; j = edge[j].next) {
if(id[i] != id[edge[j].v])
in[id[edge[j].v]] ++;
}
}
int pp = 0;
for(int i = 1; i <= scc; i ++) {
if(in[i] == 0) pp ++;
}
for(int i = 1; i <= m; i ++) {
if(in[id[a[i]]] == 0) {
ans[id[a[i]]] = 1;
}
}
int out = 0;
for(int i = 1; i <= scc; i ++) {
out += ans[i];
}
if(out == pp) {}
else out = -1;
cout << out << endl;
}
return 0;
}
从$1$号点开始$dfs$,如果$n$能被遍历到就是可以的。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500;
int n, m;
vector<int> g[maxn];
int f[maxn];
void dfs(int x) {
f[x] = 1;
for(int i = 0; i < g[x].size(); i ++) {
if(f[g[x][i]]) continue;
dfs(g[x][i]);
}
}
int main() {
while(~scanf("%d%d", &n, &m)) {
for(int i = 1; i <= n; i ++) {
g[i].clear();
f[i] = 0;
}
while(m --) {
int x, y;
scanf("%d%d", &x, &y);
g[x].push_back(y);
}
dfs(1);
if(f[n]) printf("Yes\n");
else printf("No\n");
}
return 0;
}
G - 老子的意大利炮呢
可以枚举三种配件按什么顺序获得,得到之后再走到终点即可。最后阶段可以bfs。
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x7FFFFFFF;
const int maxn = 110;
char s[maxn][maxn];
int dis[maxn][maxn];
int ans;
int n, m;
int sx, sy;
int x[5], y[5];
int ex, ey;
int t[5];
int dir[4][2] = {
{-1, 0},
{1, 0},
{0, -1},
{0, 1},
};
int out(int x, int y) {
if(x < 0 || x >= n) return 1;
if(y < 0 || y >= m) return 1;
return 0;
}
void bfs() {
queue<int> q;
q.push(ex * m + ey);
dis[ex][ey] = 0;
while(!q.empty()) {
int top = q.front();
q.pop();
int nowx = top / m;
int nowy = top % m;
for(int i = 0; i < 4; i ++) {
int tx = nowx + dir[i][0];
int ty = nowy + dir[i][1];
if(out(tx, ty)) continue;
if(s[tx][ty] == '#') continue;
if(dis[nowx][nowy] + t[0] + t[1] + t[2] + 1 > dis[tx][ty]) continue;
dis[tx][ty] = dis[nowx][nowy] + t[0] + t[1] + t[2] + 1;
q.push(tx * m + ty);
}
}
}
int D(int x1, int y1, int x2, int y2) {
return abs(x1 - x2) + abs(y1 - y2);
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i ++) {
scanf("%s", s[i]);
for(int j = 0; j < m; j ++) {
dis[i][j] = INF;
}
}
scanf("%d%d", &sx, &sy);
sx --; sy --;
for(int i = 0; i < 3; i ++) {
scanf("%d%d", &x[i], &y[i]);
x[i] --;
y[i] --;
}
scanf("%d%d", &ex, &ey);
ex --; ey --;
for(int i = 0; i < 3; i ++) {
scanf("%d", &t[i]);
}
bfs();
/*
for(int i = 0; i< n; i ++) {
for(int j = 0; j < m; j ++) {
printf("%d ", dis[i][j]);
}
printf("\n");
}
*/
ans = INF;
for(int i = 0; i < 3; i ++) {
if(dis[x[i]][y[i]] == INF) continue;
int tmp = 0;
int p0, p1, p2;
if(i == 0) {
p0 = 0;
p1 = 1;
p2 = 2;
} else if(i == 1) {
p0 = 1;
p1 = 0;
p2 = 2;
} else {
p0 = 2;
p1 = 1;
p2 = 0;
}
tmp = min(D(sx, sy, x[p1], y[p1]) * 1
+ D(x[p1], y[p1], x[p2], y[p2]) * (t[p1] + 1)
+ D(x[p2], y[p2], x[p0], y[p0]) * (t[p1] + t[p2] + 1),
D(sx, sy, x[p2], y[p2]) * 1
+ D(x[p1], y[p1], x[p2], y[p2]) * (t[p2] + 1)
+ D(x[p1], y[p1], x[p0], y[p0]) * (t[p1] + t[p2] + 1)
);
tmp += dis[x[i]][y[i]];
ans = min(ans, tmp);
}
cout << ans << endl;
return 0;
}
H - 老子的全排列呢
可以手写$dfs$生成,也可以调用函数。
#include <bits/stdc++.h>
using namespace std;
int a[10];
int main() {
for(int i = 1; i <= 8; i ++) {
a[i] = i;
}
do {
for(int i = 1; i <= 8; i ++) {
cout << a[i];
if(i < 8) cout << " ";
}
cout << endl;
} while(next_permutation(a + 1, a + 9));
return 0;
}