图,树3

差分约束系统

题目大意

给定一个数轴上的 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[ai1]+ci,相当于图中插入了 a i − 1 a_i-1 ai1 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 0sum[i]sum[i1]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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值