A - 区间选点 II
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
使用差分约束系统的解法解决这道题
Input
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
Output
输出一个整数表示最少选取的点的个数
Sample Input
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
Sample Output
6
思路:规定sum[i]表示[0,i]内的点数。对于区间[ai,bi],需要满足sum[bi]>=sum[ai-1]+ci
,则问题可以转化为图:sum[ai]作为点,两点之间的点数作为线,需要求最长路,单源最长路用spfa,原点为0点,就是最小点1前面的虚拟点。还必须满足条件0<=sum[i]-sum[i-1]<=1
。
#include <iostream>
#include <queue>
using namespace std;
const int MAXM = 300000, MAXN = 50000 + 10, inf=0x7fffffff;
struct Edge
{
int to, next, w;
}Edges[MAXM];
int head[MAXN], tot, MAX = -1, sum[MAXN];
bool vis[MAXN];
void addEdge(int u, int v, int w)
{
Edges[tot].to = v;
Edges[tot].w = w;
Edges[tot].next = head[u];
head[u] = tot++;
}
void spfa(int s)
{
queue<int> q;
for (int i = 0; i <= MAX; i++) sum[i] = -inf;
q.push(0); sum[0] = 0;; vis[0] = true;
while (q.size())
{
int u = q.front(); q.pop(); vis[u] = false;
for (int i = head[u]; i != -1; i = Edges[i].next)
{
Edge e = Edges[i];
int to = e.to, w = e.w;
if (sum[u] + w > sum[to])
{
sum[to] = sum[u] + w;
if (!vis[to])
{
q.push(to);
vis[to] = true;
}
}
}
}
}
int main()
{
memset(head, -1, sizeof(int) * MAXN);
int n; cin >> n;
while (n--)
{
int u, v, w;
cin >> u >> v >> w;
v++;
addEdge(u, v , w);
MAX = MAX > v ? MAX : v;
}
for (int i = 0; i <= MAX; i++)
{
addEdge(i, i + 1, 0);
addEdge(i + 1, i, -1);
}
spfa(0);
cout << sum[MAX];
}
总结:发现16进制给int赋值的时候是当作补码的。
int inf=0xffffffff==-1(×)
int inf=0x7fffffff(√)
B - 猫猫向前冲
众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。
Input
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。
Output
给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
Sample Input
4 3
1 2
2 3
4 3
Sample Output
1 2 4 3
思路:拓扑排序问题,一次从队列里拿出入度为0的点,入度为0的含义是他没有前置条件了,拿出点后意味着这点作为条件已经完成了,那么将它的临界点入度减一,如果入度为0则入队列。为了满足编号小的队伍在前的条件,用优先队列,优先队列默认大根堆,注意比较函数用greater。
#include <iostream>
#include <queue>
#include <functional>
using namespace std;
const int MAXN = 500 + 5, MAXM = 5000;
struct Edge
{
int to, next;
}Edges[MAXM];
int head[MAXN], tot, deg[MAXN], N, ans[MAXN], index;
void addEdge(int u, int v)
{
Edges[tot].to = v;
Edges[tot].next = head[u];
head[u] = tot++;
}
void init()
{
index = tot = 0;
memset(head, -1, sizeof(int) * (N + 1));
}
void toposort()
{
priority_queue <int, vector<int>, greater<int> >q;
for (int i = 1; i <= N; i++)
if (deg[i] == 0) q.push(i);
while (q.size())
{
int u = q.top(); q.pop();
ans[index++] = u;
for (int i = head[u]; i != -1; i = Edges[i].next)
{
int to = Edges[i].to;
if (--deg[to] == 0) q.push(to);
}
}
}
void output()
{
for (int i = 0; i < N; i++)
{
cout << ans[i];
if (i != N - 1) cout << " ";
}
cout << '\n';
}
int main()
{
int M;
while (cin >> N >> M)
{
init();
for (int i = 1; i <= M; i++)
{
int u, v; cin >> u >> v;
addEdge(u, v);
deg[v]++;
}
toposort();
output();
}
}
C - 班长竞选
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
Input
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。
Output
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!
Sample Input
2
4 3
3 2
2 0
2 1
3 3
1 0
2 1
0 2
Sample Output
Case 1: 2
0 1
Case 2: 2
0 1 2
思路:互相py的同学看作一个强连通图,他们互相承认,看作一个整体即可。把所有强连通图缩为点,构成新的图。一个人的票数等于缩点内出了他自己的人数,和指向他的点(这个指向具有继承关系)人数的总和。答案一定是出度为0的缩点。
第一步要求强连通图,方法是kosaraju 算法,具体是这样的:
- 第一遍dfs,求出逆后序
- 第二遍dfs,按照反图逆后序遍历,把每个连通图标上号,并顺便记录下每个连通图的点数sum[i]和出度deg[i],
- 遍历每一个缩点,找到出度为0的,对这个点dfs,记录下他的的票,如果大于ans,更新,并记录下这个缩点的编号,记录在vector a种。
最后输出阶段,输出ans,然后遍历每个点,如果他所在缩点编号在a里,输出。
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 5000 + 5, MAXM = 30000 + 5;
vector<int> G1[MAXN], G2[MAXN], G3[MAXN];
int N, M, dfn[MAXN], dcnt, c[MAXN], scnt, sum[MAXN], deg[MAXN], ans, temp, order;
vector<int> a;
bool vis[MAXN] ,vis2[MAXN], vis3[MAXN];
void init()
{
for (int i = 0; i < N; i++)
{
G1[i].clear();
G2[i].clear();
G3[i].clear();
deg[i] = sum[i] = 0;
vis[i] = false;
c[i] = -1;
}
a.clear();
dcnt = scnt = ans = 0;
}
void dfs1(int x)
{
vis[x] = true;
for (auto y : G1[x])
if (!vis[y]) dfs1(y);
dfn[dcnt++] = x;
}
void dfs2(int x)
{
c[x] = scnt;
sum[scnt]++;
for (auto y : G2[x])
{
if (c[y] == -1) dfs2(y);
else if (c[y] != scnt && !vis2[c[y]])
{
deg[c[y]]++, vis2[c[y]] = true;
G3[scnt].push_back(c[y]);
}
}
}
void dfs3(int x)
{
temp += sum[x];
for (auto y : G3[x])
if (!vis3[y]) vis3[y] = true, dfs3(y);
}
void kosaraju()
{
for (int i = 0; i < N; i++)
if (!vis[i]) dfs1(i);
for (int i = N - 1; i >= 0; i--)
if (c[dfn[i]] == -1)
{
memset(vis2, 0, sizeof(bool) * N);
dfs2(dfn[i]), scnt++;
}
for (int i = 0; i <= scnt - 1; i++)
if (deg[i] == 0)
{
memset(vis3, 0, sizeof(bool) * N);
temp = 0, dfs3(i);
if (temp - 1 > ans)
{
ans = temp - 1;
a.push_back(i);
}
}
}
void output()
{
cout << "Case " << ++order << ": " << ans << '\n';
int flag = false;
for (int i = 0; i < N; i++)
for (auto f : a)
if (c[i] == f)
{
if (flag) cout << " ";
else flag = true;
cout << i;
}
cout << '\n';
}
int main()
{
cin.tie(0);
ios::sync_with_stdio(false);
int K; cin >> K;
while (K--)
{
cin >> N >> M;
init();
while (M--)
{
int u, v; cin >> u >> v;
G1[u].push_back(v);
G2[v].push_back(u);
}
kosaraju();
output();
}
}