1.P4017 最大食物链计数
确实比较板子,但是考验思维,我愿称之为思维拓扑排序
作为拓扑的第一题,确实没想到要进行思维处理
这里考虑用一种dp的思维方式(?
多引入两个变量,一个记录出度,一个记录每一个点的种类数量,这样就可以一直满足不同方向过来所产生的种类数量
出度的缘由是这里需要绝对的两端(我开始以为是大于二的食物链都行,那样的话就每一次操作都+1就好)
贴代码(不要忘记取模,不然只有两个点捏)
// Problem:
// P4017 最大食物链计数
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4017
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int N=5010;
vector<int> e[N];
int ans;
int din[N];//入度
int oin[N];//出度
int n,m;
int f[N];//每个点的数量
void toposort(){
queue<int> q;
for(int i=1;i<=n;++i){
if(din[i]==0) q.push(i),f[i]=1;
}
while(q.size()){
auto k=q.front();q.pop();
for(auto t:e[k]){
if(--din[t]==0) q.push(t);
f[t]=(f[k]+f[t])%80112002;
}
}
for(int i=1;i<=n;++i){
if(oin[i]==0) ans=(f[i]+ans)%80112002;
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b;cin>>a>>b;
e[a].push_back(b);
++din[b];
++oin[a];
}
toposort();
cout<<ans%80112002<<endl;
return 0;
}
2.P2712 摄像头
这道题比较板子,在操作的时候,如果pop就ans--即可,有几个坑点
输出yes 摄像头的范围 摄像头所能照到的位置不一定会存在摄像头(怎么会有题卡这种鬼地方)
// Problem:
// P2712 摄像头
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2712
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int N=505;
vector<int> e[N];
int ans=0;
int din[N];
bool check[N];
vector<int> kkk(N);
void toposort(){
queue<int> q;
for(int i=1;i<=ans;++i){
if(din[kkk[i]]==0) q.push(kkk[i]);
}
while(q.size()){
auto t=q.front();
q.pop();
//cout<<t<<endl;
//cout<<ans<<endl;
//cout<<endl;
if(check[t]) ans--;
for(auto x:e[t]){
if(--din[x]==0) q.push(x);
}
}
}
int main(){
int n;cin>>n;
ans=n;
int cnt=0;
while(n--){
int a,b;cin>>a>>b;
if(!check[a]) kkk[++cnt]=a,check[a]=true;
while(b--){
int k;cin>>k;
e[a].push_back(k);
din[k]++;
}
}
toposort();
if(ans==0) cout<<"YES"<<endl;
else cout<<ans<<endl;
return 0;
}
3.P1137 旅行计划
这道题比上一道题要简单不少,与第一题思路相同,但这一题的想法就是维护一个max就好了,我其实没有太看懂提,但是这么操作不管有几个起始点,都可以获得最大的值(看来自各个方向哪个大就选哪个方向)
// Problem:
// P1137 旅行计划
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1137
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int N=1e5+10;
vector<int> e[N];
int din[N];
int f[N];
int n,m;
void toposort(){
queue<int> q;
for(int i=1;i<=n;++i){
if(din[i]==0) q.push(i);
f[i]=1;
}
while(q.size()){
auto t=q.front();
q.pop();
for(auto x:e[t]){
if(--din[x]==0) q.push(x);
f[x]=max(f[t]+1,f[x]);
}
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b;cin>>a>>b;
e[a].push_back(b);
din[b]++;
}
toposort();
for(int i=1;i<=n;++i) cout<<f[i]<<endl;
return 0;
}
4.P1960 郁闷的记者
这道题难过的令人无语,调试了一整天才发现是读错题了。这题面真的阴间.......
我的想法是开两个堆分别维护最大字典序和最小字典序,如果答案不同说明有不同情况(极端情况)
贴代码
// Problem:
// P1960 郁闷的记者
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1960
// Memory Limit: 500 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int N=1e4+10;
int cnt1[N];//记录名次
int cnt2[N];
int n,m;
vector<int> e[N];
int din1[N];
int din2[N];
void toposort(){
priority_queue<int> a;
priority_queue<int,vector<int>,greater<int>> b;
for(int i=1;i<=n;++i){
//cout<<din1[i]<<endl;
if(din1[i]==0){
a.push(i);b.push(i);//cout<<i<<endl;
}
}
int now=0;
while(a.size()){
auto t=a.top();
a.pop();
cnt1[t]=++now;
//cout<<t<<endl;
//cout<<t<<endl;
for(auto x:e[t]){
if(--din1[x]==0) a.push(x);
}
}
now=0;
while(b.size()){
auto t=b.top();
b.pop();
cnt2[t]=++now;
cout<<t<<endl;
for(auto x:e[t]){
if(--din2[x]==0) b.push(x);
}
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b;cin>>a>>b;
e[a].push_back(b);
din1[b]++;din2[b]++;
}
toposort();
//for(int i=1;i<=n;++i) cout<<cnt2[i]<<endl;
for(int i=1;i<=n;++i){
if(cnt1[i]!=cnt2[i]){
cout<<1<<endl;
return 0;
}
}
cout<<0<<endl;
return 0;
}
5.P6145 [USACO20FEB] Timeline G
一遍就AC,想的时间也是最短的。
但是这道题确实比较难想,要是没跟我说这是拓扑排序我不一定能想到。这道题比较隐晦,我的思路是一个类似dp的操作(每一次都更新最大),也就是每一次都要更新到最大。
这里使用拓扑排序的原因,我总结为:每一个的结局时间都是不确定的,要先确定前面的才能确定后面的,这个过程就是一个拓扑序。
在这个拓扑序遍历的时候,对每一次进行更新(对某个点有影响的所有点(已经没有入度了)继续更新),语言无力,请看代码。(我没看懂题解区说的递推是啥)
// Problem:
// P6145 [USACO20FEB] Timeline G
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6145
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n,m,s;
const int N=1e5+10;
int f[N];
vector<pair<int,int>> e[N];
int din[N];
void toposort(){
queue<int> q;
for(int i=1;i<=n;++i){
if(din[i]==0) q.push(i);
}
while(q.size()){
auto t=q.front();
q.pop();
for(auto x:e[t]){
int a=x.first,b=x.second;
f[a]=max(f[a],f[t]+b);//每次都更新一下
if(--din[a]==0) q.push(a);
}
}
}
int main(){
cin>>n>>m>>s;
for(int i=1;i<=n;++i) cin>>f[i];
while(s--){
int a,b,c;cin>>a>>b>>c;
e[a].push_back({b,c});
din[b]++;
}
toposort();
for(int i=1;i<=n;++i) cout<<f[i]<<endl;
return 0;
}
6.P1807 最长路
这道题有点坑,有不少遭点,一开始我以为可以直接copy上一题代码(还是太想去睡觉了),结果发现必须是从1到n的路径。
这里有几个坑点
1.从1到n 且题目已经告诉你路径一定是从小节点到大节点的,枉我想了这么久的环
2.这里的权值可以是负数,所以应该把2-n的所有编程-1e9
3.由于指定了起始位置,还会存留一下入度为0的值,对后面的数字造成不良影响,对此,我们可以把这些先一步捞出来处理一下就好,见代码
好像是dp的方法吧,话说感觉越来越板了,拓扑都是这样的题目,每道题都要结合一点dp思想来着
上代码
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n,m,s;
const int N=2000;
const int M=5e4+10;//数据不要开错了
int f[N];
vector<pair<int,int>> e[M];
int din[N];
bool check;
void toposort(){
queue<int> q;
for(int i=2;i<=n;++i){
if(din[i]==0) q.push(i);
f[i]=-1e9;
}
while(q.size()){//多一个处理,减少这些不会入队的值对入队的影响
auto t=q.front();
q.pop();
for(auto x:e[t]){
int a=x.first;
if(--din[a]==0) q.push(a);
}
}
q.push(1);
while(q.size()){
auto t=q.front();
q.pop();
if(t==n) check=true;
for(auto x:e[t]){
int a=x.first,b=x.second;
f[a]=max(f[a],f[t]+b);
if(--din[a]==0) q.push(a);
}
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b,c;cin>>a>>b>>c;
e[a].push_back({b,c});
din[b]++;
}
toposort();
if(check) cout<<f[n]<<endl;
else cout<<-1<<endl;
return 0;
}
7.P1113 杂务
这道题小细节比较多,我调试了一个钟,但是思路与前面又很大的相似之处,其实好像又是dp思想,难度不大,直接贴代码
// Problem:
// P1113 杂务
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1113
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n;
const int N=1e4+10;
int din[N];
int f[N];
int now[N];
vector<int> e[N];
int ans=0;
void toposort(){
queue<int> q;
for(int i=1;i<=n;++i){
if(din[i]==0) q.push(i);
}
while(q.size()){
auto t=q.front();
q.pop();
ans=max(ans,now[t]);
for(auto x:e[t]){
now[x]=max(now[x],now[t]+f[x]);
//cout<<now[x]<<endl;
//cout<<1<<endl;
if(--din[x]==0) q.push(x);
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>i;
int k;
cin>>f[i];
now[i]=f[i];
while(cin>>k){
if(k==0) break;
else{
e[k].push_back(i);din[i]++;
//cout<<i<<' '<<din[i]<<endl;
}
}
}
//for(int i=1;i<=n;++i) cout<<din[i]<<endl;
toposort();
cout<<ans<<endl;
return 0;
}
绿题,比较考验思维,我在看题解前,大致想到的思路是,相邻的数据中,找不同的从而进行比较(没有考虑到,点的等级关系只在起点到终点中有关,从而想假了)
大致想法是,在一段中,出现的点一定比未出现的等级高,因为比出现的点等级更高的点一定会出现(有点绕)
此外,用一点dp思想,处理一下等级就好(前几道题就在干这事)
代码如下
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int n,m;
const int N=1100;
vector<int> e[N];
bool check[N][N];
int f[N];
int din[N];
void toposort(){
queue<int> q;
for(int i=1;i<=n;++i){
if(din[i]==0) q.push(i);
}
int maxn=0;
while(q.size()){
auto t=q.front();q.pop();
maxn=max(maxn,f[t]);
for(auto x:e[t]){
if(--din[x]==0) q.push(x);
f[x]=max(f[t]+1,f[x]);
}
}
cout<<maxn+1<<endl;
}
int main(){
cin>>n>>m;
while(m--){
int x;cin>>x;
vector<bool> a(n+1);
vector<int> b(x+2);
for(int i=1;i<=x;++i){
cin>>b[i];a[b[i]]=1;
}
for(int j=b[1];j<=b[x];++j){
if(a[j]) continue;
for(int k=1;k<=x;++k){
if(!check[b[k]][j]) e[b[k]].push_back(j),check[b[k]][j]=1,din[j]++;
}
}
}
toposort();
return 0;
}