A题:
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点,使用差分约束系统的解法解决这道题!
实现代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=5e5+5;
const int inf=1e8+8;
struct edge
{
int to,next,w;
}e[maxn];
int head[maxn],tol;
void add(int u,int v,int w)//前向星存储图结构
{
e[++tol].to=v;
e[tol].next=head[u];
e[tol].w=w;
head[u]=tol;
}
int sum[maxn];//表示数轴上[s,t]之间选点的个数
int dis[maxn],vis[maxn];
int n;int minn;int maxx;
queue<int> q;
void spfa(int s,int t)//最长路
{
while(q.size()) q.pop();
for(int i=s;i<=t+1;i++)
{
dis[i]=-inf;
vis[i]=0;
sum[i]=0;
}
q.push(s);
dis[s]=0;
vis[s]=1;
while(!q.empty())
{
int x=q.front();q.pop();
vis[x]=0;sum[x]++;
if(sum[x]>maxx-minn+1)//判断终点
{
return ;
}
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;int w=e[i].w;
if(dis[y]<dis[x]+w)
{
dis[y]=dis[x]+w;
if(!vis[y])
{
q.push(y);
vis[y]=1;
}
}
}
}
}
int main()
{
memset(head,0,sizeof(head));
tol=0;
scanf("%d",&n);
minn=inf;maxx=0;
for(int i=0;i<n;i++)//dis[b]-dis[a]<=ci
{
int a,b,c;//区间左右端点
scanf("%d%d%d",&a,&b,&c);
minn=min(minn,a);
maxx=max(maxx,b);
add(a,b+1,c);
}
for(int i=minn;i<=maxx;i++)//0<=dis[i]-dis[i-1]<=1
{
add(i,i+1,0);
add(i+1,i,-1);
}
spfa(minn,maxx+1);
printf("%d\n",dis[maxx+1]);
return 0;
}
B题:
众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。
实现代码:
#include<iostream>
#include<stdio.h>
#include<queue>
#include<functional>
#include<string.h>
using namespace std;
const int maxn=1e3+5;
struct edge
{
int to,next;
}e[maxn];
int head[maxn],tol;
int inside[maxn];int ans[maxn];int cnt;
int n,m;
void add(int x,int y)
{
e[++tol].to=y;
e[tol].next=head[x];
head[x]=tol;
}
priority_queue<int,vector<int>,greater<int> > q;//优先队列
void toposort()//拓扑排序
{
while(q.size()) q.pop();
for(int i=1;i<=n;i++)
{
if(inside[i]==0)
{
q.push(i);
}
}
while(!q.empty())
{
int x=q.top(); q.pop();
ans[cnt]=x;cnt++;
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
inside[y]--;
if(inside[y]==0)
{
q.push(y);
}
}
}
if(cnt==n)
{
for(int i=0;i<n;i++)
{
if(i<n-1)
{
printf("%d ",ans[i]);
}
else
{
printf("%d\n",ans[i]);
}
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,0,sizeof(head));
memset(inside,0,sizeof(inside));
tol=0;
cnt=0;
for(int i=0;i<m;i++)
{
int p1,p2;
scanf("%d%d",&p1,&p2);
add(p1,p2);
inside[p2]++;
}
toposort();
}
return 0;
}
C题:
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
实现代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=3e5+5;
struct edge
{
int to,next;
}e[maxn],e1[maxn];//e1原图,e2反图
int inside[maxn];
int head[maxn],tol;int head1[maxn],tol1;
int n,m;
void add(int x,int y)
{
e[++tol].to=y;
e[tol].next=head[x] ;
head[x]=tol;
}
void add1(int x,int y)
{
e1[++tol1].to=y;
e1[tol1].next=head1[x] ;
head1[x]=tol1;
}
int vis[maxn];
int dfn[maxn];int dcnt;//后序序列 dfn[i]dfs后序列中第i个点
void dfs(int x)
{
vis[x]=1;
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(!vis[y])
{
dfs(y);
}
}
dfn[++dcnt]=x;
}
int c[maxn],scnt;int sum[maxn];//c[i]i号点所在的scc编号 sum[i]记录每个scc的数的数目
void dfs1(int x)
{
c[x]=scnt;
sum[scnt]++;
for(int i=head1[x];i;i=e1[i].next )
{
int y=e1[i].to;
if(!c[y])
{
dfs1(y);
}
}
}
void kosaraju()
{
dcnt=scnt=0;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
memset(sum,0,sizeof(sum));
for(int i=0;i<n;i++)//第一遍dfs
{
if(!vis[i]) dfs(i);
}
for(int i=n;i>=1;i--)//第二遍dfs
{
if(!c[dfn[i]])
{
++scnt;
dfs1(dfn[i]);
}
}
}
int ans[maxn];
void dfs_ans(int x,int o)
{
vis[x]=1;
for(int i=head1[x];i;i=e1[i].next)
{
int y=e1[i].to;
if(!vis[y])
{
vis[y]=1;
ans[o]+=sum[y];
dfs_ans(y,o);
}
}
}
int re[maxn];
int main()
{
int T;
scanf("%d",&T);
int num=0;
while(T--)
{
num++;
memset(head,0,sizeof(head));tol=0;
memset(head1,0,sizeof(head1));tol1=0;
memset(re,0,sizeof(re));
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);//原图
add1(b,a);//反向图,将边进行反向
}
kosaraju();
memset(head1,0,sizeof(head1));tol1=0;
memset(inside,0,sizeof(inside));
for(int i=0;i<n;i++)//缩点
{
for(int j=head[i];j;j=e[j].next)
{
int y=e[j].to;
if(c[i]!=c[y])//不属于同一个scc
{
add1(c[y],c[i]);
inside[c[i]]++;
}
}
}
memset(ans,0,sizeof(ans));int result=0;
for(int i=1;i<=scnt;i++)
{
if(inside[i]==0)
{
memset(vis,0,sizeof(vis));
dfs_ans(i,i);//其他scc中的点
ans[i]+=sum[i]-1;//去除自身的当前scc中的点
}
if(ans[i]>result)
{
result=ans[i];
}
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=scnt;i++)
{
if(ans[i]==result)
{
vis[i]=1;
}
}
printf("Case %d: %d\n",num,result);//输出数据编号及最高票数
int r=0;
for(int i=0;i<n;i++)
{
if(vis[c[i]])
{
re[r]=i;
r++;
}
}
for(int i=0;i<r;i++)
{
if(i<r-1)
{
printf("%d ",re[i]);
}
else
{
printf("%d\n",re[i]);
}
}
}
return 0;
}