A - 区间选点 II
题意:
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
思路:
使用差分约束,构造不等式组,核心如下:
- 用sum[i]表示数轴上[0,i]之间的选点的个数。
- 对于第i个区间,sum[bi]-sum[ai-1]>=ci
- 0<=sum[i]-sum[i-1]<=1
- 可以把不等式组转成图跑最长路,ans=sum[bn]
总结:
这道题本来使用的普通版的spfa,但是出现了种种错误,最后咨询了舍友,他使用了
SLF优化,我觉得挺好的,修改代码后顺利通过。
代码:
#include <iostream>
#include <cstdio>
#include <climits>
#include <algorithm>
#include <deque>
using namespace std;
const int N = 50000 + 10;
struct node
{
int to, next, w;
};
node Edges[10 * N];
int head[N], sum[N], n, a, b, c, tot, mmin, mmax;
bool vis[N];
inline void add(int x, int y, int w)
{
tot++;
Edges[tot].to = y;
Edges[tot].w = w;
Edges[tot].next = head[x];
head[x] = tot;
}
void slfspfa(int s)
{
deque<int> q;
q.push_back(s);
sum[s] = 0, vis[s] = true;
while (!q.empty())
{
int x = q.front();
q.pop_front();
vis[x] = false;
for (int i = head[x]; i; i = Edges[i].next)
{
int y = Edges[i].to;
if (sum[y] < sum[x] + Edges[i].w)
{
sum[y] = sum[x] + Edges[i].w;
if (!vis[y])
{
if (!q.empty() && sum[y] >= sum[q.front()])
q.push_back(y);
else
q.push_front(y);
vis[y] = true;
}
}
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d%d", &a, &b, &c);
add(a - 1, b, c);
mmin = min(mmin, a);
mmax = max(mmax, b);
}
for (int i = 0; i <= mmax; i++)
sum[i] = -1;
for (int i = mmin - 1; i < mmax; i++)
{
add(i, i + 1, 0);
add(i + 1, i, -1);
}
slfspfa(mmin - 1);
cout << sum[mmax] << endl;
//system("pause");
}
B - 猫猫向前冲
题意:
众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。
思路:
这道题就是拓扑排序,也没啥坑。使用小根堆优先队列即可。
总结:
拓扑排序的代码需要好好掌握。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <climits>
#include <queue>
#include <vector>
using namespace std;
const int N = 500 + 10;
struct edge
{
int v, nxt;
} Edges[N * 2];
int n, m, tot, tot2, a, b;
int indegree[N], head[N], ans[N];
void add(int x, int y)
{
tot++;
Edges[tot].v = y;
Edges[tot].nxt = head[x];
head[x] = tot;
}
bool topo()
{
priority_queue<int> q;
while (!q.empty())
q.pop();
for (int i = 1; i <= n; i++)
if (indegree[i] == 0)
q.push(-i);
while (!q.empty())
{
int u = -q.top();
q.pop();
ans[++tot2] = u;
for (int i = head[u]; i; i = Edges[i].nxt)
if (--indegree[Edges[i].v] == 0)
q.push(-Edges[i].v);
}
return tot2 == n;
}
int main()
{
while (scanf("%d%d", &n, &m) == 2)
{
tot = 0;
tot2 = 0;
memset(head, 0, sizeof(head));
memset(indegree, 0, sizeof(indegree));
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &a, &b);
add(a, b);
indegree[b]++;
}
topo();
for (int i = 1; i < n; i++)
printf("%d ", ans[i]);
printf("%d\n", ans[n]);
}
//system("pause");
}
C - 班长竞选
题意:
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
思路:
这道题学长讲了思路,主要思路:
- 计算强连通分量
- 缩点
- 计算出度为0的点的ans
总结:
我觉得这个题思路不难,难在代码的小细节,代码逻辑。
需要好好的画个草图,仔细的分析。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 5010;
const int maxm = 30020;
int f[maxn], vis[maxn], scc[maxn], color[maxn], degree[maxn];
int n, fcnt, tot1, tot2, tot3, s, scnt, mmax = 0;
int head1[maxn], head2[maxn], head3[maxn], ans[maxn], ano[maxn];
struct Edge
{
int v, nxt;
} Edges1[maxm], Edges2[maxm], Edges3[maxn];
void init()
{
tot1 = tot2 = tot3 = scnt = mmax = 0;
fcnt = -1;
memset(degree, 0, sizeof(degree));
memset(head1, -1, sizeof(head1));
memset(head2, -1, sizeof(head2));
memset(head3, -1, sizeof(head3));
memset(vis, 0, sizeof(vis));
memset(color, 0, sizeof(color));
memset(scc, 0, sizeof(scc));
memset(f, 0, sizeof(f));
memset(ano, 0, sizeof(ano));
memset(ans, 0, sizeof(ans));
}
void addedge(Edge *Edges, int *head, int u, int v, int &tot)
{
Edges[tot].v = v;
Edges[tot].nxt = head[u];
head[u] = tot++;
}
void dfs1(int x) //逆后序列
{
vis[x] = 1;
for (int i = head1[x]; i != -1; i = Edges1[i].nxt)
{
int v = Edges1[i].v;
if (!vis[v])
dfs1(v);
}
f[++fcnt] = x;
}
void dfs2(int x)
{
color[x] = scnt;
scc[scnt]++;
for (int i = head2[x]; i != -1; i = Edges2[i].nxt)
{
int v = Edges2[i].v;
if (color[v] == 0)
dfs2(v);
}
}
void dfs3(int x)
{
vis[x] = 1;
for (int i = head3[x]; i != -1; i = Edges3[i].nxt)
{
int v = Edges3[i].v;
if (vis[v] == 0)
{
ans[s] += scc[v];
dfs3(v);
}
}
}
void Kosaraju()
{
dfs1(0);
for (int i = 1; i < n; i++)
{
if (!vis[i])
dfs1(i);
}
for (int i = 0; i <= fcnt; i++)
{
int x = f[fcnt - i];
if (color[x] == 0)
{
scnt++;
dfs2(x);
}
}
}
void indent()
{
for (int i = 0; i < n; i++)
{
for (int j = head2[i]; j != -1; j = Edges2[j].nxt)
{
int v = Edges2[j].v;
if (color[i] != color[v])
{
addedge(Edges3, head3, color[i], color[v], tot3);
degree[color[v]]++;
}
}
}
}
void answer()
{
for (int i = 1; i <= scnt; i++)
{
if (degree[i] == 0)
{
memset(vis, 0, sizeof(vis));
ans[i] += scc[i] - 1;
s = i;
dfs3(i);
mmax = max(mmax, ans[i]);
}
}
for (int i = 1; i <= scnt; i++)
{
if (ans[i] == mmax)
ano[i] = 1;
}
}
void print()
{
printf("%d\n", mmax);
int flag = 0;
for (int i = 0; i < n; i++)
{
if (ano[color[i]] == 1)
{
if (flag == 0)
{
printf("%d", i);
flag++;
}
else
printf(" %d", i);
}
}
cout << endl;
}
int main()
{
int T;
cin >> T;
for (int i = 1; i <= T; i++)
{
int m;
scanf("%d%d", &n, &m);
init();
while (m--)
{
int a, b;
scanf("%d%d", &a, &b);
addedge(Edges1, head1, a, b, tot1);
addedge(Edges2, head2, b, a, tot2);
}
cout << "Case " << i << ": ";
Kosaraju();
indent();
answer();
print();
}
//system("pause");
}