给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
最近学的差分约束,这道题也要求使用差分约束做(我也想知道还有啥其他方式做),那么我们要求最小值就是构造大于号以及求最大路了
#include
#include
using namespace std;
const int nmax=5E5+10;
const int inf=1E9;
int dis[nmax],inq[nmax],head[nmax],cnt[nmax];
int n,m,k,tmp,tot=0;
struct edge{
int to,next,w;
}edges[nmax];
void add(int x,int y,int z){
edges[++tot].to=y,edges[tot].w=z;
edges[tot].next=head[x],head[x]=tot;
}
void init(){
fill(dis,dis+nmax,-inf);
fill(inq,inq+nmax,0);
//fill(cnt,cnt+nmax,1);
}
void spfa(int st){//最长路
init();
queue<int> que;
dis[st]=0;inq[st]=1;
que.push(st);
while(!que.empty()){
int x=que.front();que.pop();inq[x]=0;
for(int i=head[x];i;i=edges[i].next){
int y=edges[i].to,w=edges[i].w;
if(dis[y]<dis[x]+w){
dis[y]=dis[x]+w;
cnt[y]=cnt[x]+1;
//if(cnt[y]>=n)
if(!inq[y]) {que.push(y);inq[y]=1;}
}
}
}
}
int main(){
init();
scanf("%d",&n);
int a,b,c,end=-1;
for(int i=0;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
if(b+1>end) end=b+1;
add(a,b+1,c);
}
for(int i=1;i<=end;i++){
add(i-1,i,0);
add(i,i-1,-1);
}
spfa(0);
printf("%d",dis[end]);
return 0;
}
众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
Sample Input
4 3
1 2
2 3
4 3
Sample Output
1 2 4 3
这就是一个拓扑排序的题目,每个输入就是一个优先边,使用上课讲的kahn算法,一个像bfs一样的算法就能解决.首先遍历所有的边,将入度为0的放到最小堆中,之后每次取出堆顶,并且遍历他的所有的为出度的边,将每个到的点的入减1,相当于删去了这条边,并检查此时到的点的入度,为0则放入堆,直到堆为空,最小堆保证了最小的序列
#include
#include
#include
using namespace std;
const int nmax=5E2+10;
const int inf=1E9;
int head[nmax],cnt[nmax];
int n,m,k,tmp,tot=0;
vector<int> res;
struct edge{
int to,next,w;
}edges[nmax];
void add(int x,int y,int z){
edges[++tot].to=y,edges[tot].w=z;
edges[tot].next=head[x],head[x]=tot;
}
void init(){
//fill(dis,dis+nmax,-inf);
//fill(inq,inq+nmax,0);
fill(cnt,cnt+nmax,0);
fill(head,head+nmax,0);
tot=0;
res.clear();
}
void kahn(){
priority_queue<int> heap;
for(int i=1;i<=n;i++){if(cnt[i]==0) heap.push(-i);}
while(!heap.empty()){
int x=-heap.top();heap.pop();
res.push_back(x);
for(int i=head[x];i;i=edges[i].next){
int y=edges[i].to;
--cnt[y];
if(!cnt[y]) heap.push(-y);
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
int a,b;
for(int i=0;i<m;++i){
scanf("%d%d",&a,&b);
++cnt[b];
add(a,b,1);
}
kahn();
for(auto iter=res.begin();iter!=res.end();++iter){
printf(iter==res.begin()?"%d":" %d",(*iter));
}
printf("\n");
}
return 0;
}
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0
对于每组数据,第一行输出 “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
这道题就是一个先求出所有的强连通分量,后将分量缩点,后遍历的题目
1.首先使用kosaraju算法,这个算法可以通过逆后序序列求出所有的强连通分量
求得逆后续序列于vetcor里:
void dfs(int st){
if(vis[st]) return ;
vis[st]=1;
for(int i=head[st];i;i=edges[i].next){
dfs(edges[i].to);
}
res.push_back(st);
}
使用kosaraju,遍历逆后序序列,使用dfs每次遍历到的都是同一个强连通分量,遍历后标记在c数组中
调用:
for(auto iter=res.begin();iter!=res.end();++iter){
int key=*iter;
if(!c[key]) {scnt++;cc[scnt]=1;dfs_scc(key);}
}
遍历
void dfs_scc(int st){
c[st]=scnt;
for(int i=head_revr[st];i;i=revr[i].next){
int y=revr[i].to;
if(!c[y]) {dfs_scc(y);++cc[scnt];}
}
}
这样后我们就可以得到一个标记好1-n的分量了,我们再开一个图,进行缩点
for(int i=0;i<n;++i){
for(int j=head[i];j;j=edges[j].next){
if(c[i]!=c[edges[j].to]){
st.insert(make_pair(c[edges[j].to],c[i]));
}
}
}
for(auto it=st.begin();it!=st.end();it++){
add(it->first,it->second,3);
//printf("%d->%d\n",it->first,it->second);
}
遍历所有的点,如果不是同一个连通分量就建立边,用set防止重复的边,并且标记出读,因为最后的结果最多的点一定是出度为零的
之后遍历所有出度为0的点,计算出他们的票数
#include
#include
#include
#include
#include
using namespace std;
const int nmax=5E4+10;
const int inf=1E9;
int head[nmax],vis[nmax],head_revr[nmax],c[nmax],cc[nmax],dis[nmax],ak[nmax];
int myhead[nmax];
int n,m,tot1=0,tot2=0,myround,scnt=0,mytot=0;
vector<int> res;
set<pair<int,int> > st;
struct edge{
int to,next,w;
}edges[nmax],revr[nmax],myedge[nmax],*ped;
void add(int x,int y,int z){
int *hh,*tot;
if(z==1){ped=edges,hh=head,tot=&tot1;}
else if(z==2){ped=revr,hh=head_revr,tot=&tot2;}
else {ped=myedge,hh=myhead,tot=&mytot;dis[y]++;}
(*tot)++;
ped[*tot].to=y;
ped[*tot].next=hh[x],hh[x]=*tot;
}
void init(){
st.clear();
res.clear();
fill(ak,ak+n+1,0);
fill(myhead,myhead+n+1,0);
fill(head,head+n+1,0);
fill(vis,vis+n+1,0);
fill(head_revr,head_revr+n+1,0);
fill(c,c+n+1,0);
fill(cc,cc+n+1,0);
fill(dis,dis+n+1,0);
tot1=1,tot2=1,scnt=0,mytot=0;
}
void dfs(int st){
if(vis[st]) return ;
vis[st]=1;
for(int i=head[st];i;i=edges[i].next){
dfs(edges[i].to);
}
res.push_back(st);
}
void dfs_scc(int st){
c[st]=scnt;
for(int i=head_revr[st];i;i=revr[i].next){
int y=revr[i].to;
if(!c[y]) {dfs_scc(y);++cc[scnt];}
}
}
int dfs_get(int st){
if(vis[st]) return 0;
int ans=0;
vis[st]=1;
for(int i=myhead[st];i;i=myedge[i].next){
ans+=dfs_get(myedge[i].to);
}
return ans+cc[st];
}
void kosaraju(){
for(int i=0;i<n;++i) dfs(i);
reverse(res.begin(),res.end());//得到后逆序列
for(auto iter=res.begin();iter!=res.end();++iter){
int key=*iter;
if(!c[key]) {scnt++;cc[scnt]=1;dfs_scc(key);}
}
//for(int i=1;i<=scnt;i++) printf("dis[%d]=%d\n",i,dis[i]);
for(int i=0;i<n;++i){
for(int j=head[i];j;j=edges[j].next){
if(c[i]!=c[edges[j].to]){
st.insert(make_pair(c[edges[j].to],c[i]));
}
}
}
for(auto it=st.begin();it!=st.end();it++){
add(it->first,it->second,3);
//printf("%d->%d\n",it->first,it->second);
}
int mymax=-1;
for(int i=1;i<=scnt;i++){
if(!dis[i]) { fill(vis,vis+n+1,0);ak[i]=dfs_get(i);mymax=max(mymax,ak[i]);}
}
printf("%d\n",mymax-1);
bool flag=true;
for(int i=0;i<n;++i){
int tmp_scnt=c[i];
if(ak[tmp_scnt]==mymax) {
if(flag){
printf("%d",i);
flag=false;
}
else printf(" %d",i);
}
}
}
int main(){
int a,b;
scanf("%d",&myround);
for(int kp=1;kp<=myround;++kp){
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;++i){
scanf("%d%d",&a,&b);
add(a,b,1);
add(b,a,2);
}
printf("Case %d: ",kp);
kosaraju();
printf("\n");
}
return 0;
}
ps:其实我一开始写的是另一个,在做强连通分量的时候缩点,感觉应该可以,但是没过,求指教!代码如下:
#include
#include
#include
#include
using namespace std;
const int nmax=5E4+10;
const int inf=1E9;
int head[nmax],vis[nmax],head_revr[nmax],c[nmax],cc[nmax],dis[nmax];//cc[i]是每个强连通分量的中含有点的个数,dis是指向他的最大票数的强连通分量的的票数
int n,m,tot1=0,tot2=0,myround,scnt=0;
vector<int> res;
struct edge{
int to,next,w;
}edges[nmax],revr[nmax],*ped;
void add(int x,int y,int z){
int *hh,*tot;
if(z==1){ped=edges,hh=head,tot=&tot1;}
else {ped=revr,hh=head_revr,tot=&tot2;}
(*tot)++;
ped[*tot].to=y;
ped[*tot].next=hh[x],hh[x]=*tot;
}
void init(){
res.clear();
fill(head,head+n+1,0);
fill(vis,vis+n+1,0);
fill(head_revr,head_revr+n+1,0);
fill(c,c+n+1,0);
fill(cc,cc+n+1,0);
fill(dis,dis+n+1,0);
tot1=1,tot2=1,scnt=0;
}
void dfs(int st){
if(vis[st]) return ;
vis[st]=1;
for(int i=head[st];i;i=edges[i].next){
dfs(edges[i].to);
}
res.push_back(st);
}
void dfs_scc(int st){
c[st]=scnt;
for(int i=head_revr[st];i;i=revr[i].next){
int y=revr[i].to;
if(!c[y]) {dfs_scc(y);++cc[scnt];}
else if(c[y]!=scnt) dis[scnt]=max(dis[scnt],cc[c[y]]+dis[c[y]]);//防止循环吃自己家的
}
}
void kosaraju(){
for(int i=0;i<n;++i) dfs(i);
reverse(res.begin(),res.end());//得到后逆序列
//for(auto it=res.begin();it!=res.end();it++) printf("%d ",*it);
for(auto iter=res.begin();iter!=res.end();++iter){
int key=*iter;
if(!c[key]) {scnt++;cc[scnt]=1;dfs_scc(key);}
}
//for(int i=1;i<=scnt;i++) printf("dis[%d]=%d\n",i,dis[i]);
int mymax=-1;
vector<int> ans;
for(int i=scnt;i>0;--i){
mymax=max(mymax,dis[i]+cc[])
}
printf("%d\n",mymax-1);
bool flag=true;
for(int i=0;i<n;++i){
int tmp_scnt=c[i];
if(cc[tmp_scnt]+dis[tmp_scnt]==mymax) {
if(flag){
printf("%d",i);
flag=false;
}
else printf(" %d",i);
}
}
}
int main(){
int a,b;
scanf("%d",&myround);
for(int kp=1;kp<=myround;++kp){
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;++i){
scanf("%d%d",&a,&b);
add(a,b,1);
add(b,a,2);
}
printf("Case %d: ",kp);
kosaraju();
printf("\n");
}
return 0;
}