//DPS(深度搜索)
//n-皇后问题
//方法一(与数字全排列相似)
#include<bits/stdc++.h>
using namespace std;
const int N = 80;
int n,res=0;
char Q[N][N];
bool cow[N],dg[N],rdg[N];//dg,rdg是对角线和反对角线,cow是列;
void dfs(int u)
{
if(u==n)
{
res++;
for(int i=0;i<n;i++) puts(Q[i]);
cout<<endl;
return;
}
for(int i=0;i<n;i++)
if(!cow[i]&&!dg[u+i]&&!rdg[n-u+i])//u+i是,根据对角线规律,每个对角线的点都等于行数+上列数.n-u+i是防止i-u为负数;
{
Q[u][i]='Q';
cow[i]=dg[u+i]=rdg[n-u+i]=true;
dfs(u+1);
Q[u][i]='.';
cow[i]=dg[u+i]=rdg[n-u+i]=false;//恢复现场
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
Q[i][j]='.';
dfs(0);//从第0行开始;
cout<<res;
}
//方法二 原始方法,通俗易懂,全局搜索
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n;
char Q[N][N];
bool cow[N],dg[N],rdg[N],row[N];//cow 列,row 行;
void dfs(int x,int y,int q)
{
if(y==n) y=0,x++;
if(x==n)
{
if(q==n)
{
for(int i=0;i<n;i++) puts(Q[i]);
cout<<endl;
}
return;
}
// 不放皇后
dfs(x,y+1,q);
// 放皇后
if(!cow[y]&&!row[x]&&!dg[x+y]&&!rdg[x-y+n])
{
Q[x][y]='Q';
cow[y]=row[x]=dg[x+y]=rdg[x-y+n]=true;
dfs(x,y+1,q+1);
cow[y]=row[x]=dg[x+y]=rdg[x-y+n]=false;
Q[x][y]='.';
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
Q[i][j]='.';
dfs(0,0,0);//行,列,已经放置的皇后数目;
return 0;
}
//BFS(最短路径)
//例题链接:https://vjudge.net/problem/POJ-3984;
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int g[N][N];
int d[N][N];
typedef pair<int,int> PII;
PII q[N*N],p[N][N];
int bfs()
{
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
q[0]={0,0};
memset(d,-1,sizeof d);
d[0][0]=0;
int tt=0,hh=0;
while(hh<=tt)
{
auto t=q[hh++];
for(int i=0;i<4;i++)
{
int x=t.first+dx[i],y=t.second+dy[i];
if(x<n&&x>=0&&y<m&&y>=0&&d[x][y]==-1&&g[x][y]==0)
{
d[x][y]=d[t.first][t.second]+1;
p[x][y]=t;
q[++tt]={x,y};
}
}
}
int x=n-1,y=m-1;
while(x||y)
{
cout<<x<<","<<y<<endl;
auto t=p[x][y];
x=t.first;
y=t.second;
}
return d[n-1][m-1];
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>g[i][j];
cout<<bfs()<<endl;
return 0;
}
//方法二,stl((队列方法),基本思想,步骤与方法一相同.记录下所有空格的最短路径,然后输出所需空格的最短路径.
#include<bits/stdc++.h>
using namespace std;
int mp[5][5],vis[5][5];//map记录地图,vis记录这一点是否被访问过
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//向右,向左,向上,向下移动
struct node{
int x,y;//记录点的坐标
};
node pre[10][10];//记录某个节点的父节点
void BFS()
{
queue<node> que;//STL queue的应用
node str;
str.x=str.y=0;
que.push(str);//(0,0)入队
vis[0][0]=1;//标记(0,0)已被访问
while(!que.empty())//队列不空时
{
node now=que.front();//用一个结点记录队首结点
que.pop();//出队
if(now.x==4&&now.y==4)//当前 访问的结点为(4,4),即全部访问完
return;
for(int i=0;i<4;i++)
{
node next;
next.x=now.x+dir[i][0];//x往左或者往右移动
next.y=now.y+dir[i][1];//y往上或者往下移动
if(next.x>=0&&next.x<5&&next.y>=0&&next.y<5&&mp[next.x][next.y]==0&&vis[next.x][next.y]==0)
//点next(x,y)在地图里,且地图里所对应的点是路而不是墙壁,且该点未被访问
{
vis[next.x][next.y]=1;//标记为已访问
que.push(next);//下一个结点入队
pre[next.x][next.y]=now;//记录一下next(next.x,next.y)这个结点的父节点是now
}
}
}
}
void print(node cur)//倒序输出
{
if(cur.x==0&&cur.y==0)
{
printf("(0, 0)\n");
return;
}
print(pre[cur.x][cur.y]); //递归输出这个结点父节点 ,最终的结果先输出(0,0)
printf("(%d, %d)\n",cur.x,cur.y);//打印这个结点的坐标
}
int main()
{
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)
scanf("%d",&mp[i][j]);
BFS();
node ed;
ed.x=ed.y=4;//定义结点ed是图的最右下角那个结点
print(ed);
return 0;
}
///朴素dijkstra算法 —— 模板题 AcWing 849. Dijkstra求最短路 I
///时间复杂是 O(n2+m)O(n2+m), nn 表示点数,mm 表示边数
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,m;
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);//将每个点每个距离赋值为无穷大
dist[1] = 0;
for (int i = 0; i < n - 1; i ++ )
{
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
// 用t更新其他点的距离
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
memset(g,0x3f,sizeof g);
cin>>n>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);
}
int t=dijkstra();
cout<<t<<endl;
}
///堆优化版dijkstra —— 模板题 AcWing 850. Dijkstra求最短路 II
///时间复杂度 O(mlogn)O(mlogn), nn 表示点数,mm 表示边数
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int h[N],e[N],ne[N],idx,n,m,w[N];
int dist[N];
bool st[N];
typedef pair<int ,int>PII;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;/// 邻接表存储所有边
}
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
priority_queue<PII,vector<PII>,greater<PII>> heap;//创立小根堆
heap.push({0,1});/// first存储距离,second存储节点编号
while(!heap.empty())
{
auto t=heap.top();
heap.pop();
int ver=t.second,dis=t.first;
if(st[ver]) continue;
for(int i=h[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dis+w[i])
{
dist[j]=dis+w[i];
heap.push({dist[j],j});
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
int t=dijkstra();
cout<<t<<endl;
}
///最短路计数,使用邻接表动态数组,bfs
#include<bits/stdc++.h>
using namespace std;
const int N=1000010,mod=100003;
vector<int> g[N];
int dist[N];
bool vis[N];
int cnt[N];
int n,m;
void bfs()
{
memset(dist,0x3f3f3f,sizeof dist);
memset(vis,false,sizeof vis);
queue<int>que;
dist[1]=0;
cnt[1]=1;
que.push(1);
while(!que.empty())
{
int u=que.front();
que.pop();
if(vis[u]) continue;
vis[u]=true;
for(auto v:g[u])
{
if(!vis[v])
{
if(dist[v]>dist[u]+1)///如果此时不是1最短路,更新最短路
{
dist[v]=dist[u]+1;
cnt[v]=cnt[u];
cnt[v]%=mod;
que.push(v);
}
else if(dist[v]==dist[u]+1)///如果走的路是最短路,就在这个地方加上1;
{
cnt[v]+=cnt[u];
cnt[v]%=mod;
}
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b;
cin>>a>>b;
g[a].push_back(b);///储存邻接表
g[b].push_back(a);
}
bfs();
for(int i=1;i<=n;i++)
cout<<cnt[i]<<endl;
return 0;
}
///ballman-ford
///存在负边时使用
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,k;
int dist[N],backup[N];
struct node
{
int a,b,w;
}edge[N];
int ballman_ford()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<k;i++)
{
memcpy(backup,dist,sizeof dist);
for(int j=0;j<m;j++)
{
int a=edge[j].a,b=edge[j].b,w=edge[j].w;
dist[b]=min(dist[b],backup[a]+w);
}
}
if(dist[n]>0x3f3f/2) return -1;
else return dist[n];
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
edge[i]={a,b,w};
}
int t =ballman_ford();
if(t==-1) puts("impossible");
else cout<<t<<endl;
return 0;
}
///SPFA路径
///使用的邻接表储存
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m;
int h[N],e[N],ne[N],w[N],idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int>que;
que.push(1);
st[1]=true;
while(!que.empty())
{
int now=que.front();
que.pop();
st[now]=false;
for(int i=h[now];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[now]+w[i])
{
dist[j]=dist[now]+w[i];
if(!st[j])
{
que.push(j);
st[j]=true;
}
}
}
}
if(dist[n]>0x3f3f/2) return -1;
else return dist[n];
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
int t=spfa();
if(t==-1) cout<<"impossible"<<endl;
else cout<<t<<endl;
return 0;
}
///二维数组存SPFA
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
struct node
{
int b,w;
}e;
int n,m;
int dist[N];
bool st[N];
vector<node>g[N];
int spfa()
{
memset(dist,0x3f3f,sizeof dist);
dist[1]=0;
queue<int>que;
que.push(1);
st[1]=true;///表示我们的这个点已经入队列了
while(!que.empty())
{
int now=que.front();
que.pop();
st[now]=false;///队头出去了,那就是这个点不在这个队列里了标记false;
for(int i=0;i<g[now].size();i++)
{
int v=g[now][i].b;
if(dist[v]>dist[now]+g[now][i].w)
{
dist[v]=dist[now]+g[now][i].w;
if(!st[v])///如果队列里面没有他,再加入队列;
{
st[v]=true;
que.push(v);
}
}
}
}
if(dist[n]>0x3f3f/2) return -1;
else return dist[n];
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a;
cin>>a>>e.b>>e.w;
g[a].push_back(e);///存入数据
}
int t=spfa();
if(t==-1) cout<<"impossible"<<endl;
else cout<<t<<endl;
return 0;
}
///SPFA判断负环
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
struct node
{
int b,w;
}e;
int n,m;
int dist[N],cnt[N];
bool st[N];
vector<node>g[N];
bool spfa()
{
queue<int>que;
for(int i=1;i<=n;i++)///有可能从起点1,走不到负环的位置,所以1只需要把所有点都加入队列遍历即可
{
st[i]=true;
que.push(i);
}
while(!que.empty())
{
int now=que.front();
que.pop();
st[now]=false;///队头出去了,那就是这个点不在这个队列里了标记false;
for(int i=0;i<g[now].size();i++)
{
int v=g[now][i].b;
if(dist[v]>dist[now]+g[now][i].w)
{
dist[v]=dist[now]+g[now][i].w;
cnt[v]=cnt[now]+1;///这个代表的是当前这条路的边数
if(cnt[v]>=n) return true;///如果,我是说如果当前路的边数已经大于n了,那很明显存在负环;
if(!st[v])///如果队列里面没有他,再加入队列;
{
st[v]=true;
que.push(v);
}
}
}
}
return false;
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a;
cin>>a>>e.b>>e.w;
g[a].push_back(e);///存入数据
}
if(spfa()) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;
}
///floyd算法,适用于多个询问,复杂度0n3
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,k;
int d[N][N];
void floyd()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int x=1;x<=n;x++)
d[j][x]=min(d[j][x],d[j][i]+d[i][x]);
}
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) d[i][j]=0;
else d[i][j]=1e9;
while(m--)
{
int a,b,w;
cin>>a>>b>>w;
d[a][b]=min(d[a][b],w);
}
floyd();
while(k--)
{
int a,b;
cin>>a>>b;
if(d[a][b]>1e9/2) cout<<"impossible"<<endl;
else cout<<d[a][b]<<endl;
}
return 0;
}
///A-star搜索
///将bfs中的队列换成优先队列
///dist数组里除了存真实距离之外还需要存入估价距离
///3个小时理解+学新东西,要清楚这里面的估价距离是怎么算出来的,要知道哈希图函数的应用.
///此题为八数码难题,下面附上代码,下一个还有传统bfs,也是用到了哈希图;
#include<bits/stdc++.h>
//#define first x;
//#define second y;
using namespace std;
typedef pair<int,string>pis;
int f(string state)
{
int res=0;
for(int i=0;i<state.size();i++)
if(state[i]!='x')
{
int t=state[i]-'1';
res+=abs(i/3-t/3)+abs(i%3-t%3);
}
return res;
}
string bfs(string start)
{
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
char op[]="urdl";
string end="12345678x";
unordered_map<string,int >dist;
unordered_map<string,pair<char,string>> prev;
priority_queue<pis,vector<pis>,greater<pis>>heap;
dist[start]=0;
heap.push({f(start),start});
while(!heap.empty())
{
auto t=heap.top();
heap.pop();
string state=t.second;
if(state==end) break;
int x,y;
for(int i=0;i<9;i++)
if(state[i]=='x')
{
x=i/3,y=i%3;
break;
}
string source=state;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||a>=3||b<0||b>=3) continue;
state=source;
swap(state[x*3+y],state[a*3+b]);
if(!dist.count(state)||dist[state]>dist[source]+1)
{
dist[state]=dist[source]+1;
prev[state]={op[i],source};
heap.push({dist[state]+f(state),state});
}
}
}
string res;
while(end!=start)
{
res+=prev[end].first;
end=prev[end].second;
}
reverse(res.begin(),res.end());
return res;
}
int main()
{
string start,seq;
char c;
for(int i=0;i<9;i++)
{
cin>>c;
start+=c;
if(c!='x') seq+=c;
}
int num=0;
for(int i=0;i<8;i++)
for(int j=i;j<8;j++)
if(seq[i]>seq[j]) num++;
if(num%2) cout<<"unsolvable";
else cout<<bfs(start)<<endl;
return 0;
}
///传统bfs
///相较于A-star算法较为简便,但哈希图的搜索导致搜索时间大大减小
#include<bits/stdc++.h>
using namespace std;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
char op[]="durl";
string ed="12345678x";
unordered_map<string,int>dist;
unordered_map<string,string>pre;
bool bfs(string s)
{
queue<string>que;
que.push(s);
dist[s]=0;
while(!que.empty())
{
string t=que.front();
que.pop();
string now=t;///由于后面t中x的位置要变换,所有要开一个copy,用于记录t变换后,t之前的模样,一对一的hash;
if(t==ed) return true;
int k=t.find('x');///找到x的位置,进行操作
int x=k/3,y=k%3;///将一维的位置转换到二维
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||a>=3||b<0||b>=3) continue;///不满足直接出局
swap(t[k],t[a*3+b]);///进行转换
if(!dist.count(t))///如果此时这个状态没被记录过,那就记录,如果被记录过,那就证明这条路肯定不是最短的次数,就直接跳过
{
dist[t]=i;///一对一hash,记录操作
pre[t]=now;///一对一hash,记录变换前的string模样
que.push(t);
}
swap(t[k],t[a*3+b]);
}
}
return false;
}
int main()
{
string st,res;
for(int i=0;i<9;i++)
{
char c;
cin>>c;
st+=c;
}
if(bfs(st))///类似于小木棍问题,将搜索问题转化为bool函数
{
string t=ed;
while(t!=st)
{
res=res+op[dist[t]];///记住,这里string类的+=与+算法不一样,所以在字符串拼接的时候最好不用+=,+=返回的是char类型,char类型不能想加
///详见:https://blog.csdn.net/weixin_43222324/article/details/106814953?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168378661516800222855326%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168378661516800222855326&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-106814953-null-null.142^v86^control,239^v2^insert_chatgpt&utm_term=%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%2B%3D%E4%B8%8E%2B&spm=1018.2226.3001.4187
t=pre[t];
}
reverse(res.begin(),res.end());///由于是从尾到头,所以要逆序一下
cout<<res;
}
else
cout<<"unsolvable";
return 0;
}
///染色法搜索
//https://www.luogu.com.cn/record/110255899
///这种包围类型的染色类型,可以通过在本题圈外面加一圈可以被搜的点,这样就可以搜到所有可以被搜的点了
///拿此题来说明,在外面加一层0,即使边是*也能也能搜到0;详细思路在https://www.luogu.com.cn/problem/P1162
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m,res;
char mp[N][N];
bool vis[N][N];
void dfs(int x,int y)///进行染色
{
if(x<0||x>n+1||y<0||y>m+1||vis[x][y]) return;///如果这个点被染过或者这个点本来就是墙,就跳过
vis[x][y]=true;
for(int i=0;i<4;i++)
dfs(x+dx[i],y+dy[i]);
}
void dfs1(int x,int y)
{
for(int i=0;i<4;i++)
{
int x1=x+dx[i],y1=y+dy[i];
if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&!vis[x1][y1]&&mp[x1][y1]=='0')
{
res++;///每标记一个,就说明有一个存在;
vis[x1][y1]=true;
dfs(x1,y1);
}
}
return;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>mp[i][j];
if(mp[i][j]=='*') vis[i][j]=true;
}
dfs(0,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!vis[i][j])
{
res++;
vis[i][j]=true;
dfs1(i,j);
}
cout<<res;
return 0;
}
//https://www.luogu.com.cn/problem/P1077
///记忆化搜索,已经搜过的元素标记一下不需要再搜了,可以大大提高速度;
/**
所有的记忆化搜索都可以转化为动态规划,但所有的动态规划不一定能转换成记忆化搜索 --clg;
**/
#include<bits/stdc++.h>
using namespace std;
const int N=1010,mod=1e6+7;
int n,m,a[N],f[N][N];
int dfs(int st,int num)///返回值函数,定义为int
{
if(num>m) return 0;
if(st==n+2) return 0;
if(f[st][num]) return f[st][num];///如果这种方法已经搜过,直接返回这种方法的res就可以;
if(num==m) return 1;///如果达到,说明这种方法可以,所以+1;
int res=0;
for(int i=0;i<=a[st];i++) res=(res+dfs(st+1,i+num))%mod;
f[st][num]=res;///说明我这种情况已经全搜完啦,不用再搜啦,再看到就直接给你答案了;
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
cout<<dfs(1,0);
return 0;
}