week8——作业题

Problem1——区间选点

问题描述

在这里插入图片描述
在这里插入图片描述

问题分析

这个问题要求用差分约束来求解。首先将这个ab区间有c个点,转化为描述0到a点中选取的点的个数,和0到b点中选取的个数差为c来代表。再将‘sum(b)-sum(a)>=c’转化成‘sum(b)>=sum(a)+c’;在转化为图的问题,将a和b点看成是一个图里面的两个相邻顶点,其中有边从a指向b。每个顶点有自己的值。每个边也有自己的权值。建立一个图,使得每个边a,b都满足,dis(b)>=dis(a)+a_b.w;
这里还需要注意的一点就是,需要满足每个点的dis点不会比小于这个点小(套娃警告)
所以建立a,b+1的路径,和每个点和自己的+1的点的路径。
复杂度3n+m

代码
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<queue>
int n;
int a,b,c;

bool cmp(int a,int b)
{
	return a<b;
}
struct line1
{
	int x,y,z;
	int next;
}lines[250001];

int sum[50001],vis[50001],inq[50001],ceng[50001];
int head[50001];
int tot,tot2;
queue<int> que; 
void chushi()
{
	tot=0;
	
	for(int i=0;i<50001;i++)
	{
		head[i]=-1;
		sum[i]=0;
		vis[i]=0;
		inq[i]=0;
		ceng[i]=0;
	}
} 
void addline(int x,int y,int z)
{
	lines[++tot].x=x;
	lines[tot].y=y;
	lines[tot].z=z;
	lines[tot].next=head[x];
	head[x]=tot;
	
	//shu[tot]=x;
}
void bfs(int min,int max)
{
	//tot2=0;
	memset(sum,128,sizeof(sum));
	sum[0]=0;
	 que.push(0);
	//cout<<" max"<<max<<endl;
	while(!que.empty())//!que.empty() 
	{
		int xx=que.front();
		que.pop();
		//if(inq[xx]==1)continue;
		vis[xx]=0;
		//cout<<" 开始循环   "<<xx<<endl;
		for(int i=head[xx];i!=-1;i=lines[i].next)
		{
			
			if(sum[lines[i].y]<sum[xx]+lines[i].z)//更新 
			{
				//cout<<" 链接的点"<<lines[i].y<<"    z="<<sum[lines[i].y]<<endl;
				sum[lines[i].y]=sum[xx]+lines[i].z;
				if(vis[lines[i].y]==0)
				{
					que.push(lines[i].y);
					//ceng[lines[i].y]=ceng[xx]+1;
					vis[lines[i].y]=1;
					//if(ceng[lines[i].y]>n)inq[lines[i].y]=1;
				}
			}
		}
		//tot2++;
	}
	
}
int main()
{
	scanf("%d",&n);
	int min=1000000,max=0;
	chushi();
	for(int i=0;i<n;i++)
	{
		scanf("%d%d%d",&a,&b,&c);//b-a>=c
		a++;
		b++;
		addline(a-1,b,c);
		
		if(min>a)min=a;
		if(max<b)max=b;
	}
	//sort(shu,shu+n,cmp);
	//cout<<"mina="<<min<<"    maxb="<<max<<endl;
	for(int i=1;i<=max;i++) 
	{
		addline(i-1,i,0);
		addline(i,i-1,-1);
	}
	bfs(min,max);
	//for(int i=0;i<=max;i++)
	//{
	//	cout<<sum[i]<<endl;
	//}
	cout<<sum[max]<<endl;
}
遇到的问题

1.建立边的时候,出现过负数项,然后做出改变使其+1。
2.储存max,min的点的值没有保存更新的点(也就是加了一的)

Problem2——猫猫向前冲

问题描述

在这里插入图片描述
在这里插入图片描述

问题分析

采用拓扑算法,建立图,并把计算每个顶点的入度计算一遍,先将入度为0的都入队,然后再依次拓展这些点,每经过一条边,就对其涉及到的顶点的入度进行更新,再每次遇到入度重新变更成0的点入队。再次更新,直到所有的点都遍历完了。
因为要最小字典序列,所以图的边的存放,是按照点的大小排序。依次入队就能够得到结果。
复杂度(n+m)log(n)

代码
#include<iostream>
using namespace std;
#include<queue>
#include<vector>
int M,N,p1,p2,tot;
priority_queue<pair<int,int> > q;
vector<int> ans;
int in_deg[50000],head[50000],vis[50000];
struct match
{
	int u,v,next;
}matches[50000];
void init()
{
	for(int i=1;i<=N;i++)
	{
		in_deg[i]=0;
		head[i]=-1;
		vis[i]=0;
	}
}
void add(int u,int v)
{
	matches[tot].u=u;
	matches[tot].v=v;
	matches[tot].next=head[u];
	head[u]=tot;
	//cout<<"  u="<<u<<"   v="<<v<<"   "<<"    next="<<matches[tot].next<<endl;
	tot++;
	in_deg[v]++;

}
void toposort()
{
	int u;
	for(int i=1;i<=N;i++)
		{
			if(in_deg[i]==0)
			{
				q.push(make_pair(-i,i));
				//cout<<"入队  "<<i<<endl;
			}
			
		}
	while(!q.empty())
	{
		u=q.top().second;
		q.pop();
		ans.push_back(u);
		//cout<<"     出队  "<<u<<endl;
		if(vis[u]==1)
			continue;
		vis[u]=1;
		for(int i=head[u];i!=-1;i=matches[i].next)
		{
			int v=matches[i].v;
			in_deg[v]=in_deg[v]-1;
			//cout<<"     临边 "<<v<<"     "<<in_deg[v]<<endl;
			if(in_deg[v]==0)//多次算到这个点为0是不可能的的 
			{
				q.push(make_pair(-v,v));
				//cout<<"入队  "<<v<<endl;
			}
		}
	}
}
void output()
{
	for(int i=0;i<N;i++)
	{
		cout<<ans[i];
		if(i==N-1)
			cout<<endl;
		else
			cout<<" ";
	}
	while(!ans.empty())
	ans.pop_back();
	
}
int main()
{
	while(~scanf("%d%d",&N,&M))
	{
		init();
		for(int i=0;i<M;i++)
		{
			scanf("%d%d",&p1,&p2);
			add(p1,p2);
		}
		//for(int i=1;i<=N;i++)
		//{
		//	cout<<in_deg[i]<<endl;
		//}
		toposort();
		output();
	}
	
}

遇到的问题

没有及时的把vector清空,导致后面的测试数组的答案没多少变化。

Problem3——班长竞选

问题描述

在这里插入图片描述
在这里插入图片描述

问题分析

先寻找SCC(强连通图),因为他们的投票可以归结为一样的去向。然后再按照SCC再次建立图(逆序),每一个入度为0的SCC(正序)情况去再次遍历(每次遍历前vis重新赋值),计算其所能收获到的票数,即能逆序到达的所有SCC的点的数目的和。
强连通图的求法,先用深搜寻找后续的顺序(挨个存下),再次逆序遍历只要能够到达的点就是一个联通图。并在第二次遍历时直接建立SCC(都是逆序),计算in_deg的值(入度)。
复杂度,前面的建立SCC的时候只有2(n+m),后面找结果的SCC复杂度是n(n+m)

代码
#include<iostream>
using namespace std;
#include<queue>
#include<algorithm>
#include<vector>
#include<string.h>
#include<list>
int T,N,M,A,B;
int tot1,tot2,tot3;
int dcnt,fcnt,mcnt;
int head1[5010],head2[5010],vis[5010],dfn[5010],mfn[5010];
int SCC[5010],c[5010],du[5010],ans[5010];
int head3[5010];
vector<int> G[5010];
struct node
{
	int scc;
	int num;
} nodes[30010];
int tot4;

queue<int> q;
struct poll
{
	int u,v,next;
};
poll poll1[30010],poll2[30010];
poll scc_poll[30010];
bool cmp(node a,node b)
{
	if(a.scc!=b.scc)
	{
		return a.scc>b.scc;
		
	}
	else
	{
		return a.num<b.num;
	}
}
void init()
{
	tot1=0;
	tot2=0;
	tot3=0;
	tot4=0;
	for(int i=0;i<=N;i++)
	{
		head1[i]=-1;
		head2[i]=-1;
		head3[i]=-1;
		SCC[i]=0;
		mfn[i]=0;
		c[i]=0;
		dfn[i]=0;
		vis[i]=0;
		ans[i]=0;
		du[i]=0;
	}
}
void add1(int u,int v)//原图 
{
	poll1[tot1].u=u;
	poll1[tot1].v=v;
	poll1[tot1].next=head1[u];
	head1[u]=tot1;
	tot1++;
	//cout<<"边   u="<<u<<"   v="<<v<<"    tot="<<tot1-1<<"    next="<<poll1[tot1-1].next<<endl;
}
void add2(int u,int v)//反图 
{
	poll2[tot2].u=u;
	poll2[tot2].v=v;
	poll2[tot2].next=head2[u];
	head2[u]=tot2;
	tot2++;
}

void dfs1(int x)
{
	
	vis[x]=1;	
	for(int i=head1[x];i!=-1;i=poll1[i].next)
		{
			int v=poll1[i].v;
			if(vis[v]==0)
			{
				vis[v]=1;
				dfs1(v);
				
			}
		}
	dfn[dcnt]=x;
	dcnt++;	
	
}

void dfs2(int x)
{
	q.push(x);
	c[x]=fcnt;
	vis[x]=2;
	//cout<<"222   "<<x<<endl;
	//SCC[x]++;
	G[c[x]].push_back(x);
	SCC[c[x]]++;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		//cout<<"222"<<endl;
		for(int i=head2[u];i!=-1;i=poll2[i].next)
		{
			int v=poll2[i].v;
			//cout<<"   22    "<<v<<"   vis="<<vis[v]<<endl;
			if(vis[v]!=2)//同一个SCC 
			{
				q.push(v);
				c[v]=fcnt;
				vis[v]=2;
				SCC[c[v]]++;
				G[c[v]].push_back(v);
				//cout<<"v="<<v<<"    c="<<c[v]<<endl; 
			}
			if(c[v]!=c[x])
			{
				du[c[v]]++;
				//cout<<"u="<<c[u]<<"    v="<<c[v]<<endl;
				scc_poll[tot3].u=c[u];//建立SCC 逆序 
				scc_poll[tot3].v=c[v];
				scc_poll[tot3].next=head3[c[u]];
				head3[c[u]]=tot3;
				tot3++;
			}
		}
		
	}
}
void solve()	//求SCC
{
	dcnt=0,fcnt=0;
	
	for(int i=0; i<N; i++)
		if(vis[i]!=1)
			dfs1(i);
	for(int i=N-1; i>=0; i--)
	{
		//cout<<dfn[i]<<"  ? "<<vis[dfn[i]]<<endl;
		if(vis[dfn[i]]!=2)
		{
			fcnt++;
			dfs2(dfn[i]);
		}
	}
	//for(int i=0;i<=N-1;i++)
	//{
	//	cout<<c[i]<<endl;
	//}
		
	
}


void dfs4(int x)
{
	q.push(x);
	vis[x]=1;
	ans[x]=SCC[x];
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		
		for(int i=head3[u];i!=-1;i=scc_poll[i].next)
		{
			int v=scc_poll[i].v;
			//cout<<"44444"<<v<<endl;
			if(vis[v]!=1)
			{
				
				vis[v]=1;
				//cout<<"44    "<<SCC[v]<<"    "<<v<<endl;
				q.push(v);
				ans[x]+=SCC[v];
				//cout<<ans[x]<<"  !!  "<<x<<endl;
			}
		}
	}
}
int main()
{
	scanf("%d",&T);
	for(int i=0;i<T;i++)
	{
		
		scanf("%d%d",&N,&M);
		init();
		for(int j=0;j<M;j++)
		{
			scanf("%d%d",&A,&B);
			add1(A,B);
			add2(B,A);
		}
		solve();
		mcnt=0;
		for(int j=1;j<=fcnt;j++)
		{
			//cout<<"  du-"<<du[j]<<"    j="<<j<<endl;
			if(du[j]==0)
			{
				memset(vis,0,sizeof(vis));
				dfs4(j);
				//cout<<ans[j]<<"    "<<j<<"    G="<<G[j].size()<<endl;
				for(int k=0;k<G[j].size();k++)
				{
					//cout<<"456"<<endl;
					nodes[tot4].scc=ans[j];
					nodes[tot4].num=G[j][k];
					tot4++;
					//cout<<G[j][k]<<"    "<<ans[j]<<endl;
				}
			}
		}
		
		sort(nodes,nodes+tot4,cmp);
		int mm=0;
		cout<<"Case "<<i+1<<": ";
		for(int j=0;j<tot4;j++)
		{
			
			if(nodes[j].scc>mm)
			{
				mm=nodes[j].scc;
				//aaa.clear();
				//aaa.push_back(nodes[j].num);
				cout<<mm-1<<endl;
				cout<<nodes[j].num;	
				
			}
			else if(nodes[j].scc==mm)
			{
				cout<<" ";
				//aaa.push_back(nodes[j].num);
				cout<<nodes[j].num;
				
			}
			
			
		}
		//aaa.sort();
		for(int j=0;j<=N;j++)
		G[j].clear();
		//if(i!=T-1)
		cout<<endl;
		
	}
	
	return 0;
}
遇到的问题

习惯了广搜,导致写上去后,才发现后序的顺序不对。还有就是对于最后一次搜索寻找点的时候没有及时更新vis数组的值(用来判断是否到达)导致结果不对。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值