文章目录
A - 区间选点 II
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
使用差分约束系统的解法解决这道题
输入
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
输出
输出一个整数表示最少选取的点的个数
样例输入
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
样例输出
6
思路
综述
这道题考到差分约束系统;
差分约束系统共需要四步:
1、找到关系xi<=xj+k
2、建图;
3、找到源点S
4、确定求最短路还是最长路
不等式组转换
建图
如下图所示:
最长路最短路问题
过程
spfa过程和图论相同,差分约束关键在建图和最短和最长路的选择上面;
for (int i = 0; i < n; i++) {
cin >> a >> b >> c;
add(a, b + 1, c);
}
for (int i = 0; i <= 50005; i++) {
add(i, i + 1, 0);
add(i + 1, i, -1);
}
总结
若是x[b]>=x[a]+c 则是求最小值,最长路
若是x[b]<=x[a]+c 则是求最大值,最短路
代码
上文有详细注释
#include <iostream>
#include <queue>
#define inf 1000000
//#include <cstdlib>
#include <cstring>
using namespace std;
int n;
const int maxm=1e6;
struct Edge{
int v,w,nxt;
}e[maxm];
int tot=0;
const int maxn = 5e4+50;
int head[maxn];
void init(){
for(int i=0;i<5e4+50;i++){
head[i]=-1;
}
}
int ans=-inf;
int dis[maxn];
int vis[maxn];
void spfa(int s){
queue<int> qq;
memset(dis,-inf,sizeof(dis));
memset(vis,0,sizeof(vis));
qq.push(s);
while(qq.size()){
int u = qq.front();
qq.pop();
dis[s]=0;
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v,w=e[i].w;
if(dis[v]<dis[u]+w){
dis[v] = dis[u]+w;
if(!vis[v])qq.push(v),vis[v]=1;
if(ans<dis[v]){
ans = dis[v];
}
}
}
}
}
void add(int u,int v,int w){
e[tot].v = v;
e[tot].w = w;
e[tot].nxt = head[u];
head[u] = tot;
tot++;
}
int main(){
init();
cin>>n;
int a,b,c;
for(int i=0;i<n;i++){
cin>>a>>b>>c;
add(a,b+1,c);
}
for(int i=0;i<=50005;i++){
add(i,i+1,0);
add(i+1,i,-1);
}
spfa(0);
cout<<ans<<endl;
}
B - 猫猫向前冲
众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。
输入
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。
输出
给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
样例输入
4 3
1 2
2 3
4 3
样例输出
1 2 4 3
思路
综述
这是一个拓扑排序的问题
思路如下:
1、记录所有节点的入度;
2、将入度为0的所有节点入大根堆(加负号成为小根堆)
3、取堆顶的元素,将其所有的出边删除,更新入度数组,进入步骤二
过程
Step1:输入
普通的图的存储,采用前向星存储;
Step2:kahn算法
Step2.1 0度节点如堆
for (int i = 1; i <= N; i++) {
if (dee[i] == 0)qq.push(-i);
}
Step2.2 取堆顶元素–>操作
输出
if (flag) {
cout << u;
flag = 0;
}
else {
cout << " " << u;
}
更新
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].v;
dee[v]--;
if (dee[v] == 0)qq.push(-v);
}
总结
1、STL中默认为大根堆—>加负号入堆可以置为小根堆
2、多组数据之间注意初始化问题
代码
上文有详细注释
#include <iostream>
#include <queue>
using namespace std;
int N,M;
const int maxn=550;
struct Edge{
int v,nxt;
}e[maxn];
int head[maxn];
int dee[maxn];
int tot;
void init(){
for(int i=0;i<maxn;i++){
head[i]=-1;
dee[i]=0;
}
tot=0;
}
void add(int u,int v){
e[tot].v = v;
e[tot].nxt = head[u];
head[u] = tot;
tot++;
}
void kanh(){
priority_queue<int> qq;
for(int i=1;i<=N;i++){
if(dee[i]==0)qq.push(-i);
}
int flag=1;
while(qq.size()){
int u = -qq.top();
qq.pop();
if(flag){
cout<<u;
flag=0;
}else{
cout<<" "<<u;
}
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
dee[v]--;
if(dee[v]==0)qq.push(-v);
}
}
cout<<endl;
}
int main(){
while(cin>>N){
cin>>M;
init();
int a,b;
for(int i=0;i<M;i++){
cin>>a>>b;
add(a,b);
dee[b]++;
}
kanh();
}
}
C - 班长竞选
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
输入
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。
输出
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!
样例输入
2
4 3
3 2
2 0
2 1
3 3
1 0
2 1
0 2
样例输出
Case 1: 2
0 1
Case 2: 2
0 1 2
思路
综述
这道题考察SCC,强连通分量
DFS序
前序序列:dfs的遍历顺序
后序序列:dfs遍历完成的次序
逆后序序列:后序序列的逆序
Kosaraju
1、第一遍dfs确定dfs的逆后序序列
2、在反图中按照逆后序序列进行遍历,每次起点开始的连通点为一个SCC
过程
变量
G1:存原图
G2:存反图
G3:存缩点后的图
函数
dfs1
用于确定逆后序
dfs2
用于kosaraju算法确定SCC
dfs3
用于缩点
kosaraju
用于确定SCC强连通分支
topoint
缩点函数
output 输出
总结
代码
上文有详细注释
#include<iostream>
#include<queue>
#include<vector>
#include <string.h>
#include <algorithm>
using namespace std;
struct point
{
vector<int> v;
}Point[5005];
vector<int> G1[5005], G2[5005], G3[5005];
int n, sccnum[5005], dfn[5005], vis[5005], dcnt, scnt, indeg[5005];
int tag = 1;
int T;
int m;
void dfs1(int u)
{
vis[u] = 1;
for(int i = 0 ; i < G1[u].size() ; i++)
if(!vis[G1[u][i]])
dfs1(G1[u][i]);
dfn[dcnt++] = u;
}
void dfs2(int u)
{
sccnum[u] = scnt;
for(int i = 0 ; i < G2[u].size() ; i++)
if(!sccnum[G2[u][i]])
dfs2(G2[u][i]);
}
int dfs3(int u)
{
vis[u] = 1;
int v = Point[u].v.size();
for(int i = 0 ; i < G3[u].size() ; i++)
if(!vis[G3[u][i]])
v += dfs3(G3[u][i]);
return v;
}
void kosaraju()
{
dcnt = scnt = 0;
memset(sccnum, 0, sizeof sccnum);
memset(vis, 0, sizeof vis);
for(int i = 0 ; i < n ; i++)
if(!vis[i])
dfs1(i);
for(int i = n - 1 ; i >= 0; i--)
if(!sccnum[dfn[i]])
{
++scnt;
dfs2(dfn[i]);
}
}
void topoint(){
for(int i =1 ; i <= scnt ; i++)
{
G3[i].clear();
Point[i].v.clear();
indeg[i] = 0;
}
for(int i = 0 ; i < n ; i++)
{
Point[sccnum[i]].v.push_back(i);
for(int j = 0 ; j < G1[i].size() ; j++)
{
if(sccnum[i] == sccnum[G1[i][j]])
continue;
else
{
G3[sccnum[G1[i][j]]].push_back(sccnum[i]);
indeg[sccnum[i]]++;
}
}
}
}
void SET(){
for(int i = 1 ; i <= scnt ; i++)
{
sort(G3[i].begin(), G3[i].end());
G3[i].erase(unique(G3[i].begin(), G3[i].end()), G3[i].end());
}
}
void output(){
int sum[scnt + 1] = {0};
int ans[5005] = {0};
int Max = 0;
for(int i = 1 ; i <= scnt ; i++)
{
if(!indeg[i])
{
for(int j = 1 ; j <= scnt ; j++)
vis[j] = 0;
sum[i] = dfs3(i) - 1;
if(Max < sum[i])
Max = sum[i];
}
}
int num = 0;
for(int i = 1 ; i <= scnt ; i++)
{
if(sum[i] == Max)
{
for(int j = 0 ; j < Point[i].v.size() ; j++)
{
ans[num] = Point[i].v[j];
num++;
}
}
}
sort(ans, ans + num);
cout << "Case " << tag << ": " << Max << endl;
tag++;
for(int i = 1 ; i < num ; i++)
printf("%d ",ans[i-1]);
printf("%d\n",ans[num - 1]);
}
int main(){
cin >> T;
for(int qw=0;qw<T;qw++)
{
scanf("%d%d",&n,&m);
//初始化
for(int i = 0 ; i < n ; i++)
{
G1[i].clear();
G2[i].clear();
}
for(int wq=0;wq<m;wq++)
{
int a, b;
scanf("%d%d",&a,&b);
G1[a].push_back(b);
G2[b].push_back(a);
}
kosaraju();
topoint();
SET();
output();
}
}