(最大流)
EK算法:BFS来寻找增广路 Dinic:(用到层次图) FF算法:DFS来寻找增广路
(三种算法)
(EK和FF的不同,FF比较慢)
(最大流就是最小割)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#define rep(i,s,e) for(int i=s;i<=e;i++)
#define rep1(i,s,e) for(int i=s;i<e;i++)
#define rep2(i,s,e,c) for(int i=s;i>=e;i--)
#define pfi(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
#define pfn() printf("\n")
#define pfs() printf(" ")
#define sfi(x) scanf("%d",&x)
#define sfi1(x,y) scanf("%d%d",&x,&y)
#define sff(x) scanf("%lf",&x)
#define sfl(x) scanf("%lld",&x)
#define memset1(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
const int MAX = 1e4 + 50;
const int mod = 996873654;
int capacity[MAX][MAX];//容量
int flow[MAX],pre[MAX];//每个点的流量和前节点
int n,m;//边数n以及汇点m,源点是1
int BFS(int src,int des){//BFS寻找增广路
queue<int> q;
memset(pre,-1,sizeof(pre));
pre[src]=0;
flow[src]=0x3f3f3f3f;//将源点的流量设为无限
q.push(src);
while(!q.empty()){
int index = q.front();
q.pop();
if(index == des) break; //找到了增广路
rep1(i,1,m){
if(capacity[index][i]>0 && pre[i]==-1){
pre[i]=index;
flow[i]=min(capacity[index][i],flow[index]);
q.push(i);
}
}
}
//如果找到增广路返回该条增广路最大流量,否则返回-1
if(pre[des]!=-1){
return flow[des];
}
else return -1;
}
int maxFlow(int src,int des){//最大流函数
int increasement=0;
int sumflow=0;
while((increasement==BFS(src,des))!=-1){
int k = des;
while(k!=src){
int last = pre[k];
capacity[last][k]-=increasement;//改变正向边的容量
capacity[k][last]+=increasement;//改变反向边的容量
k = last;
}
sumflow+=increasement;
}
return sumflow;
}
int main(){
while(cin>>n>>m){
memset1(capacity);
memset1(flow);
rep(i,1,n){
int start,end,ci;
cin>>start>>end>>ci;
if(start == end) continue; //两点相同的情况,不过一般出题不会有这种情况吧
capacity[start][end]+=ci;//注意出现重复的情况
}
cout<<maxFlow(1,m)<<endl;
}
return 0;
}
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#define rep(i,s,e) for(int i=s;i<=e;i++)
#define rep1(i,s,e) for(int i=s;i<e;i++)
#define rep2(i,s,e,c) for(int i=s;i>=e;i--)
#define pfi(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
#define pfn() printf("\n")
#define pfs() printf(" ")
#define sfi(x) scanf("%d",&x)
#define sfi1(x,y) scanf("%d%d",&x,&y)
#define sff(x) scanf("%lf",&x)
#define sfl(x) scanf("%lld",&x)
#define memset1(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
const int MAX = 1e4 + 50;
const int mod = 996873654;
int n,m;
int used[MAX];
int capacity[MAX][MAX];
int DFS(int src,int des,int max_capacity){
if(src==des) return max_capacity;//只要找到增广路,直接返回
rep(i,1,n){
if(capacity[src][i]>0 && !used[i]){
used[i]=1;//因为只用找到一条增广路就可以,所以不用重置为0
int d=DFS(i,des,min(max_capacity,capacity[src][i]));
if(d>0){
capacity[src][i]-=d;
capacity[i][src]+=d;
return d;
}
}
}
return -1;
}
int maxFlow(int src,int des){
int flow=0;
int increasement;
memset1(used);
used[1]=1;
while((increasement=DFS(src,des,0x3f3f3f3f))!=-1){
flow+=increasement;
memset1(used);
used[1]=1;
}
return flow;
}
int main(){
int des;
while(cin>>n>>m>>des){
memset1(capacity);
rep(i,1,m){
int start,end,ci;
cin>>start>>end>>ci;
capacity[start][end]+=ci;//注意出现重复的情况
}
cout<<maxFlow(1,des)<<endl;
}
return 0;
}
/*
4 5 3
1 2 40
1 4 20
2 3 30
2 4 20
3 4 10
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#define rep(i,s,e) for(int i=s;i<=e;i++)
#define rep1(i,s,e) for(int i=s;i<e;i++)
#define rep2(i,s,e,c) for(int i=s;i>=e;i--)
#define pfi(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
#define pfn() printf("\n")
#define pfs() printf(" ")
#define sfi(x) scanf("%d",&x)
#define sfi1(x,y) scanf("%d%d",&x,&y)
#define sff(x) scanf("%lf",&x)
#define sfl(x) scanf("%lld",&x)
#define memset1(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
const int MAX = 1e4 + 50;
const int mod = 996873654;
int n,m,des;
int capacity[MAX][MAX];
int dep[MAX];//各点层次
bool build_dep(int src,int des){//BFS构建层次图
queue<int> q;
memset1(dep);
dep[src]=1;
q.push(src);
while(!q.empty()){
int index = q.front();
q.pop();
rep(i,1,n){
if(capacity[index][i]>0 && !dep[i]){
dep[i]=dep[index]+1;
q.push(i);
}
}
}
return dep[des]?true:false;
}
int DFS(int src,int des,int max_capacity){//DFS寻找增广路
if(src==des) return max_capacity;
rep(i,1,n){
if(capacity[src][i]>0 && dep[src]+1==dep[i]){
int d = DFS(i,des,min(capacity[src][i],max_capacity));
if(d>0){
capacity[src][i]-=d;
capacity[i][src]+=d;
return d;
}
}
}
return -1;
}
int maxFlow(int src,int des){
int flow=0,increasement;
if(!build_dep(src,des)) return 0;
while((increasement=DFS(src,des,0x3f3f3f3f))!=-1){
flow+=increasement;
}
return flow;
}
int main(){
int des;
while(cin>>n>>m>>des){
memset1(capacity);
rep(i,1,m){
int start,end,ci;
cin>>start>>end>>ci;
capacity[start][end]+=ci;//注意出现重复的情况
}
cout<<maxFlow(1,des)<<endl;
}
return 0;
}
/*
4 5 3
1 2 40
1 4 20
2 3 30
2 4 20
3 4 10
*/
康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。
n*n的网格,如果n为奇数,当奇数码游戏两个局面可达,当且仅当两个局面下网格中的数依次写成1行n*n-1个元素的序列后(不考虑空格),逆序对个数的奇偶性相同。
证明:空格左右移动时,写成的序列显然不变;空格上(下)移动时,相当于某个数与它后(前)边的n-1个数交换了位置,因为n-1是偶数,所以逆序对的变化也只能是偶数。
如果n为偶数,两个局面可达,当且仅当两个局面对应网格写成序列后,“逆序对数之差”和“两个局面下空格所在的行数只差”奇偶性相同。事实上,n*m网格上(n,m>=2)也服从上述两个结论之一(根据列数奇偶性分情况讨论)
总而言之,n*m数码问题的有解性判定,可以转化为归并排序求逆序对来解决。
三次以上的多项式一定可以因式分解
自己写的,也没怎么测试,错了勿怪
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#define rep(i,s,e) for(int i=s;i<=e;i++)
#define rep1(i,s,e) for(int i=s;i<e;i++)
#define rep2(i,s,e,c) for(int i=s;i>=e;i--)
#define pfi(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
#define pfn() printf("\n")
#define pfs() printf(" ")
#define sfi(x) scanf("%d",&x)
#define sfi1(x,y) scanf("%d%d",&x,&y)
#define sff(x) scanf("%lf",&x)
#define sfl(x) scanf("%lld",&x)
#define memset1(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
const int MAX = 8e3+50;
const int mod = 996873654;
int n,m;//n行m列
//假设可以走八个方向,而且直走cost为10,斜走cost为14(简单)
int xy[][2]={{1,0},{-1,0},{0,1},{0,-1},{1,1},{-1,1},{1,-1},{-1,-1}};
struct vis{
int walkable;
bool close_list;
bool open_list;
//int cost;//题目中可能会出现的代价,比如在沼泽中花费更多的体力
int fromX,fromY;
int f,g,h;//f=g+h,用曼哈顿距离估算h,g为移动代价
}mmap[MAX][MAX];
struct node{
int x,y;
node(): x(0), y(0){}
void init(int _x,int _y){
x=_x; y=_y;
}
void calculate(node des){
mmap[x][y].g=mmap[mmap[x][y].fromX][mmap[x][y].fromY].g;
if(x==mmap[x][y].fromX || y==mmap[x][y].fromY) mmap[x][y].g+=10;
else mmap[x][y].g+=14;
mmap[x][y].h=abs(x-des.x)+abs(y-des.y);
mmap[x][y].f=mmap[x][y].g+mmap[x][y].h;
}
bool judge(node p,node des){
if(!mmap[x][y].walkable || mmap[x][y].close_list) return false;
if(x>=1&&x<=n&&y>=1&&y<=m){
if(!mmap[x][y].open_list){
mmap[x][y].open_list=true;
mmap[x][y].fromX=p.x; mmap[x][y].fromY=p.y;
calculate(des);
return true;
}
else{
int newg=mmap[p.x][p.y].g;
if(p.x==x || p.y==y) newg+=10;
else newg+=14;
if(newg<mmap[x][y].g){
mmap[x][y].fromX=p.x; mmap[x][y].fromY=p.y;
mmap[x][y].g=newg;
mmap[x][y].f=mmap[x][y].g+mmap[x][y].h;
return true;
}
else return false;
}
}
}
bool operator == (const node& temp) const{
if(x==temp.x && y==temp.y) return true;
else return false;
}
bool operator < (const node& temp) const{
return mmap[x][y].f>mmap[temp.x][temp.y].f;
}
}src,des,p,p1;
priority_queue<node> q;
void Astar(){
while(!q.empty()) q.pop();
q.push(src);
mmap[src.x][src.y].open_list=true;
while(!q.empty()){
p = q.top();
q.pop();
mmap[p.x][p.y].close_list=true;
mmap[p.x][p.y].open_list=false;
if(p==des) break;
rep1(i,0,8){
p1.init(p.x+xy[i][0],p.y+xy[i][1]);
if(p1.judge(p,des)){
q.push(p1);
}
}
}
}
int main(){
while(cin>>n>>m){
rep(i,1,n){
rep(j,1,m){
sfi(mmap[i][j].walkable);
}
}
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
src.init(x1,y1); des.init(x2,y2);
Astar();
if(mmap[des.x][des.y].walkable && mmap[des.x][des.y].close_list){
stack<node> st;
p.init(des.x,des.y);
while(1){
st.push(p);
if(p==src) break;
p.init(mmap[p.x][p.y].fromX,mmap[p.x][p.y].fromY);
}
while(st.size()){
p=st.top();
st.pop();
cout<<p.x<<" "<<p.y<<"\n";
}
}
}
return 0;
}
/*
5 7
1 1 1 1 1 1 1
1 1 1 0 1 1 1
1 1 1 0 1 1 1
1 1 1 0 1 1 1
1 1 1 1 1 1 1
3 2 3 6
*/
算法题目:
A*算法概述:
采用广度优先搜索策略,在搜索过程中使用启发函数,即有大致方向的向前进虽然目标有时候不是很明确。
A*算法核心:
A*算法的关键在于启发函数,启发函数的优劣直接影响A*算法的效率。
f(n)=g(n)+h(n);
这个式子中:f(n)表示从初始状态到目标状态的估测代价。
g(n)表示从初始状态到当前状态的代价(已经确定)。
h(n)表示从当前状态到目标状态的估测代价(预测)。
其中:h(n)的好坏直接影响评估函数的好坏。
一个好的f(n)总能明确指引算法前进的方向,可以迅速的到达目标状态。
f*(n)=g*(n)+h*(n); 我们假设的从初始状态到目标状态的实际最小代价。
这个式子中:f(n)表示从初始状态到目标状态的实际代价。
g*(n)表示从初始状态到当前状态的代价(已经确定)g*(n)和g(n)是相等的。
h*(n)表示从当前状态到目标状态的实际代价。
若h(n)<=h*(n),则总能找到最优解。(当h(n)<h*(n)的时候,不可能找到一条从初始状态到达目标状态的路径。在搜索过程中使得h(n)逐渐接近h*(n),最终找到最优路径。
优点:
与广度优先搜索策略和深度优先搜索策略相比,A*算法不是盲目搜索,而是有提示的搜索。
缺点:
该算法一般要使用大量的空间用于存储已搜索过的中间状态,防止重复搜索。
用途:
从初始状态出发 =>经过一系列中间状态 =>最终到达目标状态(或者无法到达)。
该算法用于经过中间状态时候的行进策略(其中的中间状态或者由题目给出,或者在前边已经推导得出)。
IDA*算法:
迭代加深搜索算法,在搜索过程中采用估值函数,以减少不必要的搜索。
IDA*算法核心:
设置每次可达的最大深度depth,若没有到达目标状态则加深最大深度。
采用估值函数,剪掉f(n)大于depth的路径。
优点:
使用回溯方法,不用保存中间状态,大大节省了空间。
缺点:
重复搜索:回溯过程中每次depth变大都要再次从头搜索。
用途:
和A*算法大致相同。
实际上,估测函数f(n)也是剪枝的一种。
因篇幅问题不能全部显示,请点此查看更多更全内容