A-区间选点
解题过程
每个未知数对应图中的一个顶点 ,把所有的不等式都化成图中的一条边,xi-xj<=y 就是有向边<i,j>权值为y。求单源最短路可以得到最大解(无负权回路),如果是xi-xj>=y 就是有向边<j,i>权值为y,求单源最长路可以得到最小解(无正权回路)
这道题需要找最小解,以最小的点作为起点,最大的点作为终点,设sum[i]为区间[0,i]中的点数,相邻整数之间的点数需要满足-1<=sum[i]-sum[i+1]<=0;
求取最长路后输出0到最大数之间的点数,既是最少要选的点数。
vector用时比较长虽然比较方便,还是习惯一下向前星比较好。
#include <iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
struct wedge{
int v;
int w;
};
vector<wedge>g[50010];
void add(int u,int v,int w )
{ wedge e;
e.v=v;
e.w=w;
g[u].push_back(e);
}
int dis[50010],inq[50010],inf=600000000;
int maxn=0;int minn=50010;
void spfa(int s)
{ queue<int >q;
for(int i=s;i<=maxn;i++)
{ dis[i]=-inf;
inq[i]=0;
}
q.push(s);
inq[s]=1;dis[s]=0;
while(!q.empty())
{ int u=q.front();
q.pop();
inq[u]=0;
for(int i=0;i<g[u].size();i++)
{int v=g[u][i].v;
int w=g[u][i].w;
if(dis[v]<dis[u]+w)
{ dis[v]=dis[u]+w;
if(inq[v]!=1 )
{inq[v]=1;
q.push(v);
}
}
}
}
}
int main(int argc, char** argv) {
int n;
int u,v,w;
scanf("%d",&n);
for(int i=0;i<n;i++)
{scanf("%d%d%d",&u,&v,&w);
add(u,v+1,w);
maxn=max(maxn,v+1);
minn=min(minn,u);
}
for(int i=minn;i<maxn;i++)
{add(i,i+1,0);
add(i+1,i,-1);
}
spfa(minn);
cout<<dis[maxn];
return 0;
}
B - 猫猫向前冲
解题过程
拓扑排序上学期学过比较熟悉,就是从入度为零的点开始,将他们全部入队,再将与其邻接的点的入度减1如果为零就入队,直到队列为空如果没有形成环路则得到拓扑序列。因为需要将编号小的排在前面所以使用优先级队列,用普通队列储存答案。
居然没有看到多组数据……
#include <iostream>
#include<vector>
#include<cmath>
#include<queue>
#include <stdio.h>
#include<utility>
#include <cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
vector<int >g[510];
void add(int u,int v )
{
g[u].push_back(v);
}
int in_deg[510];
priority_queue<int > q;
queue<int> qans;
void toposort(int n)
{
for(int i=1;i<=n;i++)
{if(in_deg[i]==0)
q.push(-i);
}
while(!q.empty())
{ int u=-q.top();
q.pop();
qans.push(u);
for(int i=0;i<g[u].size();i++)
{ int v=g[u][i];
if(--in_deg[v]==0)
q.push(-v);
}
}
}
int main(int argc, char** argv) {
int n,m,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{memset(in_deg,0,sizeof(in_deg));
for(int i=0;i<=n;i++)
g[i].clear();
for(int i=0;i<m;i++)
{scanf("%d%d",&a,&b);
add(a,b);
in_deg[b]++;
}
toposort(n);
while(!qans.empty())
{ cout<<qans.front();
qans.pop();
if(qans.size()!=0)
cout<<" ";
}
cout<<endl;
}
return 0;
}
C - 班长竞选
解题过程
好难啊,虽然在抄模板,还把模板抄错还找不出来……最后的不忽略行末空格怎么输出还想不出来,脑子都开始掉锈了。
在有向图G中,如果两点互相可达,则称这两个点强连通,如果G中任意两点互相可达,则称G是强连通图。
Kosaraju算法
1、从点1开始进行DFS,每次在DFS回溯的过程中进行标记,得到一个后序遍历的顺序的表。
2、对反图g2按步骤1中得到的表从后往前进行DFS遍历。使用了多少次的DFS,就有多少个强联通分量。
dcnt-dfs序计数,scnt-scc计数,dfn[i] dfs后序列中第i个点,c[i]-i号点所在ssc编号
缩点
将一个ssc看成一个点,用g储存该图,遍历原图的每一条边如果边的两点不在同一ssc则向g加入该边。
票会传递所以答案一定在出度为0的点中也就是反图入度为0的点,搞错了出入度结果用样例试一直在输出0。dfs计算票数,就是每个相连接的ssc中点的个数(用num储存)的和,保存最大值,输出学生编号时输出总票数与最大票数相等的ssc点中的所有点。
#include <iostream>
#include<vector>
#include<cmath>
#include<queue>
#include <stdio.h>
#include<utility>
#include <cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
vector<int >g1[5100],g2[5100],g[5100];
int n,m;
int c[5100],vis2[5100],dfn[5100],num[5100],sum[5100],vis[5100],indeg[5100],dcnt,scnt;
void add1(int u,int v)
{
g1[u].push_back(v);
g2[v].push_back(u);
}
void dfs1(int x)
{ vis[x]=1;
for(int i=0;i<g1[x].size();i++)
{ if(!vis[g1[x][i]])
dfs1(g1[x][i]);
}
dfn[++dcnt]=x;
}
void dfs2(int x)
{ c[x]=scnt;
for(int i=0;i<g2[x].size();i++)
{ if(!c[g2[x][i]])
dfs2(g2[x][i]);
}
}
int dfs3(int x)
{ vis2[x]=1;
int sum= num[x];
for(int i=0;i<g[x].size();++i)
{ if(!vis2[g[x][i]])
sum=sum+dfs3(g[x][i]);
}
return sum;
}
void kosaraju()
{
dcnt=0;scnt=0;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
if(!vis[i])
dfs1(i);
for(int i=n-1;i>=0;i--)
if(!c[dfn[i]])
{
++scnt;
dfs2(dfn[i]);
}
}
void spoint()
{
for(int i=0;i<n;i++)
{indeg[i]=0;
g[i].clear();
}
for(int i=0;i<n;i++)
{
num[c[i]]++;
for(int j=0;j<g1[i].size();j++)
{ int v=g1[i][j];
if(c[i]!=c[v])
{g[c[v]].push_back(c[i]);
indeg[c[i]]++;
}
}
}
}
int main(int argc, char** argv) {
int t,u,v;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%d%d",&n,&m);
for(int j=0;j<n;j++)
{g1[j].clear();
g2[j].clear();
sum[j]=0;
num[j]=0;
}
for(int j=0;j<m;j++)
{ scanf("%d%d",&u,&v);
add1(u,v);
}
kosaraju();
//cout<<"?";
spoint();
int maxn=-1;
for(int k=1;k<=scnt;++k)
{
if(indeg[k]==0)
{ for(int kk=0;kk<n;kk++)
vis2[kk]=0;
sum[k]=dfs3(k);
maxn=max(sum[k],maxn);
}
}
printf("Case %d: %d\n",i,maxn-1);
int f=0;
//cout<<"?"<<maxn[c[3]];
for(int k=0;k<n;k++)
{ if(sum[c[k]]==maxn)
{ if(f==1)
printf(" %d",k);
else
{printf("%d",k);
f=1;
}
}
}
cout<<endl;
}
return 0;
}