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数组的值(用来判断是否到达)导致结果不对。