A/D
题意
区间选点升级版:给定一个数轴上的n个区间,要求在数轴上选取最少的点使得第i个区间里至少有ci个点
- 输入
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
- 输出
6
思路
- 题目定位:差分约束
- 构造不等式组:
-1. 对于第i个区间[𝑎i, 𝑏i]需要满足𝑠𝑢𝑚 𝑏i − 𝑠𝑢𝑚 𝑎i − 1 ≥ 𝑐i
-2. 0 ≤ 𝑠𝑢𝑚 𝑖 − 𝑠𝑢𝑚 𝑖 − 1 ≤ 1 - 转化为≥不等式组跑最长路,𝑠𝑢𝑚[max{𝑏i}]即为答案
总结
- 差分形式的不等式组,可以转换为图上求最短/长路的问题求解
仍然不是很明白为什么把spfa处理环的部分删了,D就能过…也许写错了???
然而A能过…略魔幻…
代码
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXN=50005;
const int INF=50005;
struct Edge{
int to,w,next;
} edges[3*MAXN];
int head[3*MAXN],tot;
void addEdge(int from,int to,int w){
edges[++tot].to=to;
edges[tot].w=w;
edges[tot].next=head[from];
head[from]=tot;
}
int N,sum[MAXN];
int mina=INF,maxb=-1;
int vis[MAXN];
void spfa(int s){
queue<int> q;
vis[s]=1,sum[s]=0;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u]; i; i=edges[i].next){
int v=edges[i].to,w=edges[i].w;
if(sum[v]<sum[u]+w){
sum[v]=sum[u]+w;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
int main(void){
scanf("%d",&N);
for(int i=0;i<N;i++){
int ta,tb,tc;
scanf("%d%d%d",&ta,&tb,&tc);
addEdge(tb,ta-1,tc);
maxb=max(tb,maxb);
mina=min(ta,mina);
}
for(int i=mina;i<=maxb;i++){
addEdge(i,i-1,0);
addEdge(i-1,i,-1);
}
for(int i=mina-1;i<=maxb;i++)
sum[i]=-INF;
spfa(maxb);
printf("%d\n",sum[mina-1]);
return 0;
}
B
题意
一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。已知每场比赛的结果,确定字典序最小的名次序列。
思路
- 问题定位:拓扑排序
- 利用算法:Kahn算法
- 思路:每次从优先队列中将入度为0的点取出,添加至答案向量中。每处理一个入度为0的点,要将该点连接的各个点的入度-1.
总结
用矩阵WA了一版,但是感觉思路是一样的,不太明白为什么换成了邻接表就AC了…
- 拓扑排序模板
代码
#include<cstdio>
#include<queue>
#include<vector>
#include<functional>
using namespace std;
const int MAXN=505;
int N,M;
int in_degree[MAXN];
vector<int> G[MAXN];
void init(){
for(int i=0;i<=N;i++){
in_degree[i]=0;
G[i].clear();
}
}
void topology_sort(){
priority_queue<int,vector<int>,greater<int>> pq;
for(int i=1;i<=N;i++)
if(in_degree[i]==0)
pq.push(i);
vector<int> ans;
while(!pq.empty()){
int p=pq.top();
pq.pop();
ans.push_back(p);
for(auto x:G[p]){
in_degree[x]--;
if(in_degree[x]==0)
pq.push(x);
}
}
for(int i=0;i<ans.size();i++){
if(i!=0) printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
int main(void){
while(scanf("%d%d",&N,&M)!=EOF){
init();
for(int i=0;i<M;i++){
int P1,P2;
scanf("%d%d",&P1,&P2);
G[P1].push_back(P2);
in_degree[P2]++;
}
topology_sort();
}
return 0;
}
- WA的版本(用的矩阵,感觉也没错???)
#include<cstdio>
#include<queue>
#include<vector>
#include<functional>
using namespace std;
const int MAXN=505;
int N,M;
int G[MAXN][MAXN],in_degree[MAXN];
void init(){
for(int i=1;i<=N;i++){
in_degree[i]=0;
for(int j=1;j<=N;j++)
G[i][j]=0;
}
}
void topology_sort(){
priority_queue<int,vector<int>,greater<int>> pq;
for(int i=1;i<=N;i++)
if(in_degree[i]==0)
pq.push(i);
vector<int> ans;
while(!pq.empty()){
int p=pq.top();
pq.pop();
ans.push_back(p);
for(int i=1;i<=N;i++){
if(G[p][i]==1){
G[p][i]=0;
in_degree[i]--;
if(in_degree[i]==0)
pq.push(i);
}
}
}
for(int i=0;i<ans.size();i++){
if(i!=0) printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
int main(void){
while(scanf("%d%d",&N,&M)!=EOF){
init();
for(int i=0;i<M;i++){
int P1,P2;
scanf("%d%d",&P1,&P2);
G[P1][P2]=1;
in_degree[P2]++;
}
topology_sort();
}
return 0;
}
C
题意
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适。
现想要知道最高票数,并给出一份候选人名单。
思路
- 问题定位:图,强连通分量的问题(SCC)
- 问题算法思想:Kosaraju算法
- 思路:
-
- Kosaraju算法步骤:
-第一遍dfs 确定原图的逆后序序列
-第二遍dfs 在反图中按照逆后序序列进行遍历,每次由起点遍历到的点即构成一个SCC
- Kosaraju算法步骤:
-
- 缩点:每一个SCC看成1个点,重构新的图和反图,并记录图的出度
-
- 最终答案一定出现在缩点图,出度为0的SCC部分。
- 答案分为2部分:1.自己SCC的内部互投 2.别的SCC投进来的
总结
Kosaraju算法模板:
代码
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int MAXN=5005;
int N;
vector<int> G1[MAXN],G2[MAXN];
int dfn[MAXN],dcnt,c[MAXN],scnt,vis[MAXN];
void dfs1(int x){
vis[x]=1;
for(auto y:G1[x])
if(!vis[y]) dfs1(y);
dfn[++dcnt]=x;
}
void dfs2(int x){
c[x]=scnt;
for(auto y:G2[x])
if(!c[y]) dfs2(y);
}
void kosaraju(){
dcnt=scnt=0;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
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]);
}
vector<int> G3[MAXN],G4[MAXN];
int out_degree[MAXN],SCC[MAXN];
void shrink(){
memset(out_degree,0,sizeof(out_degree));
memset(SCC,0,sizeof(SCC));
for(int x=0;x<N;x++){
SCC[c[x]]++;
for(auto y:G1[x]){
if(c[x]==c[y])
continue;
G3[c[x]].push_back(c[y]);
G4[c[y]].push_back(c[x]);
out_degree[c[x]]++;
}
}
}
void dfs3(int x){
vis[x]=1;
for(auto y:G4[x])
if(!vis[y]) dfs3(y);
}
vector<int> SCC_points;
void solve(){
int ans=0;
for(int i=1;i<=scnt;i++){
memset(vis,0,sizeof(vis));
if(out_degree[i]==0){
dfs3(i);
int temp=SCC[i]-1;
for(int j=1;j<=scnt;j++){
if(i!=j&&vis[j])
temp+=SCC[j];
}
if(temp>ans){
ans=temp;
SCC_points.clear();
SCC_points.push_back(i);
}
else if(temp==ans) SCC_points.push_back(i);
}
}
printf("%d\n",ans);
int pre=0;
for(int i=0;i<N;i++){
for(auto x:SCC_points){
if(c[i]==x){
if(pre==0) pre=1;
else if(pre!=0) printf(" ");
printf("%d",i);
}
}
}
printf("\n");
}
void init(){
for(int i=0;i<N;i++){
G1[i].clear();
G2[i].clear();
G3[i].clear();
G4[i].clear();
}
SCC_points.clear();
}
int main(void){
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
for(int i=1;i<=T;i++){
init();
int M;
scanf("%d%d",&N,&M);
while(M--){
int A,B;
scanf("%d%d",&A,&B);
G1[A].push_back(B);
G2[B].push_back(A);
}
kosaraju();
shrink();
printf("Case %d: ",i);
solve();
}
//fclose(stdin);
return 0;
}