差分约束系统
题目大意
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
input
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
output
输出一个整数表示最少选取的点的个数
思路
根据题意,可建立如下关系,记sum[i]为[0,i]之间的选点个数
s
u
m
[
b
i
]
≥
s
u
m
[
a
i
−
1
]
+
c
i
sum[b_i] \geq sum[a_i-1]+c_i
sum[bi]≥sum[ai−1]+ci,相当于图中插入了
a
i
−
1
a_i-1
ai−1到
b
i
b_i
bi的一条边,权值为ci
以及约束条件:
0
≤
s
u
m
[
i
]
−
s
u
m
[
i
−
1
]
≤
1
0\leq sum[i]-sum[i-1] \leq 1
0≤sum[i]−sum[i−1]≤1, 相当于在图中插入了i到i-1,权值-1 , i-1到i,权值为0的边
在图中跑最长路SPFA,原点是
m
i
n
(
a
i
)
min(ai)
min(ai), 可以得到最小的选点个数,也就是
s
u
m
[
m
a
x
(
b
i
)
]
sum[max(bi)]
sum[max(bi)]
代码
为了防止出现-1,对输入数据进行了平移(+10)
#include <iostream>
#include <queue>
#include<cstdio>
using namespace std;
//更关心不等号右边是什么,若有源点,在求出右边之后,当然可以求出xi(=xs+c)的最值
//松弛的过程,就是满足限制条件
const int M= 600000;
const int inf = 1e7;
using namespace std;
int tot=0,n=0,maxb=0,mina=inf,dis[M],head[M],inq[M];
struct edge{
int s,e,w,next;
edge(){ };
edge(int _s,int _e,int _w,int _next){
s=_s,e=_e,w=_w,next=_next;
}
}Es[M];
void init(){
tot=0;
for(int i=0;i<M;i++) head[i]=-1;
}
void addEdge(int a,int b,int c)
{
Es[++tot].e = b;
Es[tot].w = c;
Es[tot].next=head[a];
head[a]=tot;
}
queue<int>q;
void spfa(int v)
{
for(int i=mina-1;i<=maxb;i++) inq[i]=0,dis[i]=-inf;
while(q.size()) q.pop();
dis[v]=0;
q.push(v);
inq[v]=1;
while(!q.empty()){
int x = q.front(); q.pop();
inq[x]=0;
for(int j=head[x];j!=-1;j=Es[j].next){
int to =Es[j].e;
if(dis[to]<dis[x]+Es[j].w){
// cout<<"x:"<<x<<" to:"<<to<<" disto:"<<dis[to]<<" disx:"<<dis[x]<<" esw:"<<Es[j].w<<endl;
dis[to] = dis[x]+Es[j].w;
if(!inq[to]){
q.push(to);
inq[to]=1;
}
}
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
scanf("%d",&n);
int a,b,c;
init();
for(int i=0;i<n;i++) {
scanf("%d%d%d",&a,&b,&c);
a+=10,b+=10; //向右平移
addEdge(a-1,b,c); //s(a-1)+c<= s(b) 满足形式
maxb=max(maxb,b);
mina=min(mina,a);
}
for(int i=mina;i<=maxb;i++){
addEdge(i,i-1,-1);// s(i)-1<=s(i)
addEdge(i-1,i,0); //s(i-1)-0 <=s(i)
}
spfa(mina-1);
printf("%d\n",dis[maxb]);
return 0;
}
拓扑排序
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!
思路
这个直接按照拓扑排序的思路就可以了,先找入度为零的点(保存在一个优先级队列中),从这些点中弹出字典序最小的点,开始更新他邻接到的点的入度,将入度为零的点放入优先级队列中保存,直到队列为空即可。
代码
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int M=600000;
struct edge{
int s,e,next;
edge(){ };
}Es[M];
int n,m,tot=0,head[800],deg[800];
vector<int> ans;
priority_queue<pair<int,int>> pq;
void init(){
tot=0;
for(int i=0;i<=n;i++) head[i]=-1;
}
void add(int x,int y){
Es[++tot].e =y;
Es[tot].next= head[x];
head[x]=tot;
}
void topsort(){
for(int i=1;i<=n;i++){
if(!deg[i]) pq.push(make_pair(-i,i));
}
while(!pq.empty()){
int x = pq.top().second;
ans.push_back(x);
pq.pop();
for(int j=head[x];j!=-1;j=Es[j].next){
int to = Es[j].e;
if(--deg[to]==0) pq.push(make_pair(-to,to));
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m)){
ans.clear();
while(!pq.empty()) pq.pop();
memset(deg,0,sizeof deg);
init();
int x,y;
for(int i=0;i<m;i++){
scanf("%d%d",&x,&y);
add(x,y);
deg[y]++;
}
topsort();
for(int i=0;i<ans.size();i++){
if(i) printf(" %d",ans[i]);
else printf("%d",ans[i]);
}
printf("\n");
}
return 0;
}
强连通分支
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学。
思路
由于一个强连通分支中,所有人得票是一致的,所以这个题需要先求出图中的强连通分支,在求出强连通分支之后,对强联通分支进行缩点,将一个分支缩为一个点,再求出这个这个缩点之后的图的反图,反图中入度为零的点,就是原图中得分最高的连通分支。最后,需要用bfs求出这些入度为零的点,邻接到的“点的数量”(实质是对应联通分支内点的个数),邻接到“点的数量"最多的连通分支(缩点后的点),就是得票最高的人所在的联通分支。
代码
这个题要三次遍历图,所以会有大量的数组,标记变量等,一定要注意初始化!!
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
const int M=600000;
const int N=5500;
struct edge{
int s,e,next;
edge(){ };
}Es1[M],Es2[M];
int t,n,m,tot=0,head1[N],head2[N],vis[N],scc[N],fcnt=0,indeg[N],sum[N],vis2[N],ans[N],maxc=0;
vector<pair<int,int>>f;
vector<int>G[N]; //缩点后的反图
queue<int> qu;
void init(){
tot=0;
for(int i=0;i<=N;i++) head1[i]=-1,head2[i]=-1;
}
void add(int x,int y){
Es1[++tot].e =y;
Es1[tot].next= head1[x];
head1[x]=tot;
// 求反图
Es2[tot].e =x;
Es2[tot].next= head2[y];
head2[y]=tot;
}
void dfs1(int v)
{
vis[v]=1;
for(int j=head1[v];j!=-1;j=Es1[j].next){
int to = Es1[j].e;
if(!vis[to]) dfs1(to);
}
++fcnt;
f.push_back(make_pair(-fcnt,v));
}
dfs2(int v,int tag)
{
scc[v] =tag;
sum[tag]++;
for(int j=head2[v];j!=-1;j=Es2[j].next){
int to = Es2[j].e;
if(!scc[to]) dfs2(to,tag);
}
}
void bfs3(int v,int gtag)
{
vis2[v]=gtag;
while(!qu.empty()) qu.pop();
qu.push(v);
// cout<<v<<"v:"<<endl;
while(!qu.empty())
{
int x = qu.front(); qu.pop();
// cout<<x<<": ansvp: "<<ans[v]<<endl;
ans[v]+= sum[x];
// cout<<"ansvo: "<<ans[v]<<endl;
for(int i=0;i<G[x].size();i++)
{ if(vis2[G[x][i]]!=gtag){
vis2[G[x][i]] = gtag;
qu.push(G[x][i]);
}
}
}
// cout<<"ansv::"<<ans[v]<<endl;
}
void solve()
{
memset(scc,0,sizeof scc);
memset(vis,0,sizeof vis);
memset(indeg,0,sizeof indeg);
memset(sum,0,sizeof sum);
memset(vis2,0,sizeof vis2);
memset(ans,0,sizeof ans);
fcnt=0;
for(int i=0;i<n;i++){ //第一次dfs
if(!vis[i]) dfs1(i);
}
int num=0;
sort(f.begin(),f.end());//逆后序序列
for(int i=0;i<f.size();i++){
int x = f[i].second;
if(!scc[x]) dfs2(x,++num);
// cout<<x<<":"<<scc[x]<<endl;
}
for(int h=0;h<N;h++) {
if(G[h].size()) G[h].clear();
}
for(int x=0;x<n;x++){
for(int j=head1[x];j!=-1;j=Es1[j].next){
int y = Es1[j].e;
// cout<<"sizep: "<<G[scc[y]].size()<<endl;
if(scc[x]==scc[y]) continue;
else{ // y->x
G[scc[y]].push_back(scc[x]);
// cout<<y<<"zhixinag"<<x<<endl;
// cout<<scc[y]<<":"<<scc[x]<<endl;
indeg[scc[x]]++;
}
// cout<<x<<"->"<<y<<":"<<scc[y]<<": "<<G[scc[y]].size()<<endl;
}
}
int gtag=0;
for(int i=1;i<=num;i++){ // if circle ????
if(!vis2[i]&&indeg[i]==0) bfs3(i,++gtag);
}
maxc = 1;
for(int i=1;i<=num;i++){
maxc = ans[i]>ans[maxc] ? i:maxc ;
}
}
int main()
{
// freopen("in.txt","r",stdin);
scanf("%d",&t);
for(int i=1;i<=t;i++){
scanf("%d%d",&n,&m);
init();
f.clear();
int x,y;
for(int j=0;j<m;j++){
scanf("%d%d",&x,&y);
add(x,y);
}
solve();
printf("Case %d: %d\n",i,ans[maxc]-1);
for(int v=0,k=0;v<n;v++){
if(ans[scc[v]]==ans[maxc]){
if(k++) printf(" %d",v);
else printf("%d",v);
}
}
printf("\n");
}
return 0;
}