1.spfa
队列
#include
#include
#include
#include
#include
#include
using namespace std;
int n,m,s;
int end;
int head[500005];
bool vis[10005];
int dis[10005];
const int inf=2147483647;
struct p{
int next,to,dis;
};
p a[500005];
int cnt=0;
//链式前向星 这里是双向图 所以存两次
void add(int from,int to,int dis){
a[++cnt].next=head[from];
a[cnt].to=to;
a[cnt].dis=dis;
head[from]=cnt;
a[++cnt].next=head[to];
a[cnt].to=from;
a[cnt].dis=dis;
head[to]=cnt;
}
void spfa(){
queue<int> q;
for(int i=1;i<=n;i++){
dis[i]=inf;
vis[i]=0;
}
//vis是入队标记 1为入队
q.push(s); dis[s]=0;vis[s]=1;
while(!q.empty()){
int u=q.front();
q.pop(); vis[u]=0;//出队啊
for(int i=head[u];i;i=a[i].next){
int v=a[i].to;
if(dis[v]>dis[u]+a[i].dis){
dis[v]=dis[u]+a[i].dis;
//如果可以松弛,那么经过这个的需要更新
//没入对的话就入队
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
int main(){
cin>>n>>m>>s>>end;
for(int i=1;i<=m;i++){
int f,g,w;
cin>>f>>g>>w;
add(f,g,w);
}
spfa();
cout<<dis[end]<<endl;
}
2.floyd
由于复杂度的原因,私以为是比赛中不会出现的算法
但是确实相当好写,非常令人愉悦
#include
#include
#include
#include
#include
#include
int dis[104][103];
int p[104];
int ans=999999;
const int inf=9999999;
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j) dis[i][j]=inf;
}
}
for(int i=1;i<=n;i++){
int x,y,z;
cin>>x>>y>>z;
p[i]=x;
if(y) {
dis[i][y]=1;
dis[y][i]=1;
}
if(z){
dis[i][z]=1;
dis[z][i]=1;
}
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(dis[i][j]>dis[i][k]+dis[k][j]){
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
int t=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(j!=i)
t+=dis[i][j]*p[j];
}
ans=min(ans,t);
t=0;
}
cout<<ans<<endl;
return 0;
}
3.强连通分量 - tarjan
这个板子是洛谷2341(奶牛)tarjan缩点,并加了统计出度的部分,出度为0的缩点中奶牛就是最受欢迎的奶牛
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int inf=0x3f3f3f;
const int maxn=10005;
int head[maxn],dfn[maxn],low[maxn],all[maxn],id[maxn];
bool in[maxn];
int du[maxn];
int n,m,ans,cnt,tot,gg;
struct edge{
int nxt,to;
} e[maxn*20];
stack<int> s;
void add(int x,int y){
e[++cnt].to=y;
e[cnt].nxt=head[x];
head[x]=cnt;
}
void tarjan(int x){
dfn[x]=low[x]=++tot;
s.push(x);
in[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int u=e[i].to;
if(!dfn[u]){
tarjan(u);
low[x]=min(low[x],low[u]);
}
else if(in[u]) low[x]=min(low[x],low[u]);
}
int k;
if(low[x]==dfn[x]){
gg++;
do{
k=s.top();
s.pop();
in[k]=0;
id[k]=gg;
all[gg]++;
}
while(x!=k);
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
add(a,b);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;i++){
for(int j=head[i];j;j=e[j].nxt){
int u=e[j].to;
if(id[i]!=id[u]) du[id[i]]++;
}
}
int tt=0;
for(int i=1;i<=gg;i++){
if(!du[i]){
if(tt) {
cout<<0<<endl;
return 0;
}
tt=i;
}
}
cout<<all[tt]<<endl;
return 0;
}
4.洛谷P1330
染色问题
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=200005;
int n,m,ans;
struct edge{
int nxt,to;
} e[maxn];
bool use[maxn];
int col[maxn];
int head[20000],cnt;
int sum[2];
//还是链式前向星
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
//dfs
bool dfs(int node,int c){
//如果节点访问过 判断颜色
if(use[node]){
if(col[node]==c) return 1;
return 0;
}
//若未访问过 ,标记颜色,记录个数
use[node]=1;
col[node]=c;
sum[c]++;
//这个t有何用 不太懂
bool t=1;
for(int i=head[node];i;i=e[i].nxt){
//1-c改变颜色
t = t && dfs(e[i].to,1-c);
}
return t;
}
int main(){
cin >> n >>m;
for(int i=1;i<=m;i++){
int a,b;
cin >> a>>b;
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++){
if(use[i]) continue;
sum[0]=sum[1]=0;
//如果已经访问过,代表此点所在的联通块已经扫描完毕
//如果没有过,清空数组,扫描此点所在联通块
if(!dfs(i,0)){
cout << "Impossible"<<endl;
return 0;
}
//更新答案,因为是黑白染色,所以两种取最优解即可
ans += min(sum[0],sum[1]);
}
cout << ans << endl;
return 0;
}
5.1
洛谷P2661信息传递
判断成环的,也可用并查集
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int n,ans=0x3f3f3f;
//fir存储的是第一次经过该节点的序号,那么成环时
//大小 = 遍历序号-fir[node]
int nxt[200005],fir[200005];
//vis是否访问过, forevis:是否已经搜过
bool vis[200005],forevis[200005];
void dfs(int node,int c){
if(forevis[node]) return ;
//成环
if(vis[node]){
ans = min(ans,c-fir[node]);
return;
}
vis[node]=1;
fir[node]=c;
dfs(nxt[node],c+1);
forevis[node]=1;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>nxt[i];
for(int i=1;i<=n;i++){
dfs(i,0);
}
cout << ans << endl;
return 0;
}
5.2洛谷P2921
与上一题很像,是上一题的补充
不一样的是这里成环
一开始的无脑代码(什么时候能改掉无脑提交的毛病。
显然TLE
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int n,cnt;
int ans,nxt[100005];
bool vis[100005];
void ini(){
for(int i=1;i<=n;i++)
vis[i]=0;
}
void dfs(int id,int t,int s){
int tmp=nxt[id];
if(vis[tmp]){
cout<<t<<endl;
return ;
}
vis[tmp]=1;
dfs(tmp,t+1,s);
return;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin >> nxt[i];
}
for(int i=1;i<=n;i++){
ini();
vis[i]=1;
dfs(i,1,i);
}
return 0;
}
正解,与上一题有相通之处,强
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=100005;
int n,nxt[maxn],dfn[maxn],cnt;
//vis记录是否访问过,num记录如果成环,环的大小
//time是加入环时走过的步数
int vis[maxn],num[maxn],t[maxn];
int main(){
cin >> n;
for(int i=1;i<=n;i++) cin>>nxt[i];
for(int i=1;i<=n;i++){
for(int j=i,cnt=0;;j=nxt[j],cnt++){
//如果没记录过
if(!vis[j]){
vis[j]=i;
dfn[j]=cnt;
}
//如果正好在环里成环了
else if(vis[j]==i){
num[i]=cnt-dfn[j];
//时间戳
t[i]=dfn[j];
cout << cnt << endl;
break;
}
//这种情况就是预见到未来的节点会成环
//但是这个节点之前已经算过了,答案是固定的,so直接计算了
//节省时间 ,为什么不能记录下到每一个节点的答案呢?
//一开始我是这样想的,后来发现,就算记录答案,每一次也不确定
//因为你不知道那头牛在到达这里前,经历了什么......
else{
//环的大小就是那个点的大小
num[i]=num[vis[j]];
t[i]=cnt+max(0,t[vis[j]]-dfn[j]);
cout<<t[i]+num[i]<<endl;
break;
}
}
}
return 0;
}
6.洛谷P1341无序字母对
欧拉回路:
统计每一个点的度,若都是偶数,则有欧拉回路(有进有出)
若只有两个点的度为奇数,那么以这两个点为起始点有一条欧拉路径
感觉像是一笔画,以前听大佬讲过
代码还比较清楚
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int n,fa[300],mp[300][300],du[300],cnt,head,sum;
bool f=0;
char ans[300];
//用并查集判断联通
int a[300];
int find(int x){
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
void dfs(int x){
for(int i=64;i<=125;i++){
if(mp[x][i]){
mp[x][i]=mp[i][x]=0;
dfs(i);
}
}
ans[++sum]=x;
}
int main(){
cin >> n;
for(int i=64;i<=125;i++)fa[i]=i;
char x[2];
for(int i=1;i<=n;i++){
cin >> x;
//建图加边
mp[x[0]][x[1]]=1;
mp[x[1]][x[0]]=1;
//欧拉回路 统计度数
du[x[0]]++;
du[x[1]]++;
fa[find(x[1])]=find(x[0]);
}
for(int i=64;i<=125;i++){
//根 du是为了确定出现过
if(fa[i]==i && du[i]) cnt++;
}
if(cnt!=1){
f=1;
}
cnt=0;
head=0;
for(int i=64;i<=125;i++){
if(du[i]%2){
cnt++;
if(head==0) head=i;
}
}
if(cnt&&cnt!=2) f=1;
if(head==0){
for(int i=64;i<=125;i++){
if(du[i]){
head=i;
break;
}
}
}
dfs(head);
if(f){
cout<<"No Solution"<<endl;
return 0;
}
else{
for(int i=n+1;i>=1;i--){
cout<<ans[i];
}
cout<<endl;
}
return 0;
}