c++写算法题:综合

c++算法题:综合

    • 拯救公主
      • 1251:仙岛求药
    • dijkstra模板题
      • 最短路
    • 最小生成树模板
      • kruskal法
    • dfs综合体题
      • codevs 1004 四子连棋
    • dp动态规划
      • codevs 1010 过河卒
      • codevs 1014 装箱问题
    • Floyd多源最短路
      • codevs 1020 孪生蜘蛛
    • 最小生成树
      • prim算法
        • codevs 1003 电话连线
        • 最小生成树二刷
    • 注意,数组一定要初始化,因为
      • 动态规划
        • 典型求大数余类型
          • 935. Knight Dialer

拯救公主

百炼OJ, 题目链接
c++写算法题:综合_第1张图片
c++写算法题:综合_第2张图片
思路,典型的bfs问题,但需要处理的条件较多,此种题适合使用结构体来做,注意熟练使用结构体编程。
先贴上代码,以后再多写几遍,这里用到了状态压缩的方法记录宝石的数量。

#include 
#include 
#include 
#include 
using namespace std;
#define MAX 201
char a[MAX][MAX];

struct Node
{
    int x, y; //坐标。
    int num = 0; //存储宝石数目。
    int deep = 0; //搜索深度。
    Node() {
    }
    Node(int xx, int yy, int gg, int dd) : x(xx), y(yy), num(gg), deep(dd) {
    }
};

int dir[4][2] = {{0, -1},{-1, 0},{0, 1},{1, 0}};
int visited[MAX][MAX][1 << 5 - 1];   //宝石数目最大11111
int r, c, k, doorCount;
Node doors[11];

int bit1Count(int value) {
    unsigned int count = 0;
    while (value > 0) { // until all bits are zero
        if ((value & 1) == 1) // check lower bit
            count++;
        value >>= 1; // shift bits, removing lower bit
    }
    return count;
}

void printQueue(std::queue<Node> q) {
    while (!q.empty()) {
        Node node = q.front();
        q.pop();
        cout << "(" << node.x << ", " << node.y << ", "
            << bit1Count(node.num) << ", " << node.deep << ") ";
    }
    cout << endl << endl;
}

int getTarget(Node* node, Node* endNode) {
    return (node->x == endNode->x && node->y == endNode->y && bit1Count(node->num) >= k);
}

int bfs(Node* startNode, Node* endNode) {
    if (startNode == NULL || endNode == NULL) return -1;
    memset(visited, 0, sizeof(visited));
    visited[startNode->x][startNode->y][0] = 1; //置起点已经访问
    queue<Node> q;
    q.push(*startNode); //起点入队
    while (!q.empty()) {
        Node node = q.front(); //取队首元素,出队
        q.pop();
        for (int i = 0; i < 4; i++) {  // 遍历四个方向
            int newX = node.x + dir[i][0];
            int newY = node.y + dir[i][1];
            if (newX < 0 || newX >= r || newY < 0 || newY >= c) continue;  // 越界则下一个
            if (a[newX][newY] == '#' || visited[newX][newY][node.num]==1) continue; //碰到墙或者已经访问过,则下一个
            //不是墙壁并且此节点没访问过
            visited[newX][newY][node.num] = 1;
			// 当前路可走
            Node newNode(newX, newY, node.num, node.deep + 1);
            if (a[newX][newY] >= '0' && a[newX][newY] <= '4') { // 遇到宝石
                newNode.num |= 1 << (a[newX][newY] - '0');  //状态压缩记录宝石数量
            }
            if ((newNode.x == endNode->x && newNode.y == endNode->y && bit1Count(newNode.num) >= k)) {
                return newNode.deep;
            }
            // 没有达到终点,则当前点入队
            q.push(newNode);

            //如果是传送门的话就将所有其它传送门也加入搜索队列。
            if (a[newX][newY] == '$') {
                for (int j = 0; j < doorCount; j++) {
                    Node currNode = doors[j];
                    if (currNode.x == newX && currNode.y == newY) continue;
                    Node doorNode(currNode.x, currNode.y, newNode.num, newNode.deep);
                    q.push(doorNode);
                }
            }
        }
      
    }
    return -1;
}

int main() {
    int t;
    cin >> t; //t组数据
    while (t--) {
    	// Node记录坐标,宝石数目,深度
        Node *startNode = NULL, *endNode = NULL;
        cin >> r >> c >> k; //r*c矩阵,k个宝石
        doorCount = 0;
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                cin >> a[i][j];
                switch (a[i][j]) {
                case '$':  //记录所有传送门的位置。
                    doors[doorCount++] = Node(i, j, 0, 0);
                    break;
                case 'S':  // 记录起点位置
                    startNode = new Node(i, j, 0, 0);
                    break;
                case 'E':   // 记录终点位置
                    endNode = new Node(i, j, 0, 0);
                }
            }
        }
        int ans=bfs(startNode, endNode);
        ans == -1 ? (cout << "oop!" << endl) : (cout << ans << endl);
    }
    return 0;
}
#include 
#include 
#include 
#include 
using namespace std;
//16.57
struct Node{
	int x,y;  // 坐标
	int num; // 宝石个数
	int depth;  // 深度
	
	Node(){
	}
	Node(int xx,int yy,int nn,int dd) :x(xx),y(yy),num(nn),depth(dd) {}
};
int R,C,K,ct=0;
//vector> a(R,vector(C));
char a[200][200];
int dx[]={-1,1,0,0}; //上下左右
int dy[]={0,0,-1,1};
Node jewelNode[11];

int visited[200][200][1 << 5 - 1];   //宝石数目最大11111

int bit1Count(int value) {
    unsigned int count = 0;
    while (value > 0) { // until all bits are zero
        if ((value & 1) == 1) // check lower bit
            count++;
        value >>= 1; // shift bits, removing lower bit
    }
    return count;
}

int bfs(Node *startNode,Node *endNode){
	// 把startNode加入队列
	visited[startNode->x][startNode->y][0] = 1; //置起点已经访问
	queue<Node> q;  // queue q行不行?
	q.push(*startNode); //起点入队 q.push(startNode)?
	while(!q.empty()){
		Node node=q.front();q.pop();  // 取队首元素,出队
		// 对四个方向遍历
		for(int i=0;i<4;i++){
			int newx=node.x+dx[i];
			int newy=node.y+dy[i];
			// 越界或者已经访问则跳过--------------visited数组为什么是三维的: 包含了宝石的情况
			if(newx<0||newx>=R||newy<0||newy>=C||visited[newx][newy][node.num]==1) continue;
			// 碰到#则走不通
			if(a[newx][newy]=='#')  continue;
			// 其余情况均可走,标记为已经访问
			visited[newx][newy][node.num]=1;
			Node tempNode(newx, newy, node.num, node.depth+1);  // 存下当前结果
			// 碰到宝石
			if(a[newx][newy]>='0'&&a[newx][newy]<='4'){
				tempNode.num |= 1 << (a[newx][newy]-'0');
			}
			
			// 终止条件
			if(newx==endNode->x&&newy==endNode->y&&bit1Count(tempNode.num)>=K)    return tempNode.depth;
			q.push(tempNode);
			if(a[newx][newy]=='&'){ // 把所有传送门加入队列
				for(int j=0;j<ct;j++){
					if(jewelNode[j].x==newx&&jewelNode[j].y==newy) continue;
					Node tNode(jewelNode[j].x, jewelNode[j].y, tempNode.num, tempNode.depth);
                    q.push(tNode);
				}
			}
			// 如果遇到传送门
		}
		
	}
	return -1;
}

int main()
{
	int T=0;
	cin>>T;
	while(T--){
		cin>>R>>C>>K; // R*C矩阵,K种类型宝石
		// 读取字符矩阵
		
		Node *startNode=NULL,*endNode=NULL;
	
		for(int i=0;i<R;i++){
			for(int j=0;j<C;j++){
				cin>>a[i][j];
				switch(a[i][j]){
					case 'S':  startNode=new Node(i,j,0,0);break;        // 记录起点
					case 'E':  endNode=new Node(i,j,0,0);break;   // 记录终点
					case '$':  jewelNode[ct++]=Node(i,j,0,0);   // 记录宝石数量和位置
					//  数组为什么就不可以 new Node(i,j,0,0)
				}
			}
		}
		
		int ans=bfs(startNode,endNode);
		cout<<ans;
	//	ans == -1 ? (cout << "oop!" << endl) : (cout << ans << endl);


		
	}

}

1251:仙岛求药

c++写算法题:综合_第3张图片
题目链接

#include
#include 
#include
#include 
using namespace std;
#define inf 0x3f3f3f3f
int vis[100][100];
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int m,n;  //m行n列
char map[100][100];
// 定义结构体
typedef struct node{
	int x,y;
	int step;
	node() {
    }
    node(int xx, int yy, int sp) : x(xx), y(yy), step(sp){
    }
}Node; 

int bfs(Node* startNode,Node* endNode){
	if (startNode == NULL || endNode == NULL) return -1;
	// 把起点加入队列
	queue<Node*> q; 
	q.push(startNode);
	vis[startNode->x][startNode->y]=1;
	while(!q.empty()){
		Node* temp=q.front();
		q.pop();
		int newx,newy;
		for(int i=0;i<4;i++){
			newx=temp->x+dir[i][0];
			newy=temp->y+dir[i][1];
			if(newx>=0&&newx<m&&newy>=0&&newy<n){
				if(vis[newx][newy]==0&&map[newx][newy]!='#'){  // 未访问过 
					Node* newNode=new Node(newx, newy,temp->step + 1);
					vis[newx][newy]=1;
					if(newx==endNode->x&&newy==endNode->y)
                		return newNode->step;           
					q.push(newNode);
				//	cout<x<<"  "<y<
				}
			}
		}
		
	}
	return -1;
} 
int main(){
	// 读取字符矩阵 
	while(scanf("%d%d",&m,&n)!=EOF&&(m||n))  {
		Node* startNode=NULL;
		Node* endNode=NULL;
	
		string s="";
		// 初始化地图并记录地图起点和终点 
		for(int i=0;i<m;i++){
			for(int j=0;j<n;j++){
				cin>>map[i][j];
				if(map[i][j]=='@') startNode=new Node(i,j,0);
				else if(map[i][j]=='*') endNode=new Node(i,j,0);
			}
		} 
		int ans=bfs(startNode, endNode);
   		cout << ans << endl;		
  	}		
}



dijkstra模板题

最短路

题目链接

c++写算法题:综合_第4张图片dijkstra算法:

算法思路

  • 指定一个节点,例如我们要计算 ‘A’ 到其他节点的最短路径

  • 引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,注意 如上图所示,A->C由于没有直接相连 初始时为∞)

  • 初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0,
    U集合初始时为 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞

  • 从U集合中找出路径最短的点,加入S集合,例如 A->D = 2

  • 更新U集合路径,if ( ‘D 到 B,C,E 的距离’ + ‘AD 距离’ < ‘A 到 B,C,E 的距离’ ) 则更新U

  • 循环执行 4、5 两步骤,直至遍历结束,得到A 到其他节点的最短路径

链接:https://www.jianshu.com/p/ff6db00ad866

算法模板

  1. 求邻接矩阵
  2. 求dist矩阵
  3. 更新邻接矩阵

输入用例:

5 7
1 2 4
1 4 2
2 4 1
2 3 4
3 4 1
3 5 3
4 5 7

输出用例:

6

c++写算法题:综合_第5张图片
单源最短路模板

#include
#include 
#include
using namespace std;
#define inf 0x3f3f3f3f


int main(){
	// 输入n结点,m条路径
	int adjMatrix[110][110],dist[110],vis[110];
	int n,m;
	cin>>n>>m;
	// 题目要求找到1号点到n号点的最短距离
	// 输入起点,终点,距离
	int a,b,c;
	// 初始化邻接矩阵
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j) adjMatrix[i][j]=0;
			else adjMatrix[i][j]=inf;
		}
	} 
	// 读取输入给邻接矩阵赋值 
	for(int i=1;i<=m;i++){
		cin>>a>>b>>c;
		adjMatrix[a][b]=adjMatrix[b][a]=c;
	} 
	
	// 初始化dist矩阵
	for(int i=1;i<=n;i++){
		dist[i]=inf;
	}
	for(int i=1;i<=n;i++){
		if(dist[i]>adjMatrix[1][i])
			dist[i]=adjMatrix[1][i];
	} 
	
	vis[1]=1;
	// 
	for(int i=1;i<=n;i++){
		int mindis=inf,j=1;
		for(int i=1;i<=n;i++){  // 找到剩下的所有点中离起点最近的点,置已访问 
			if(vis[i]==0&&mindis>dist[i]){
				mindis=dist[i];
				j=i;
			}
		}
		vis[j]=1;
		// 更新dist矩阵
		for(int i=1;i<=n;i++){
			if(dist[i]>adjMatrix[j][i]+dist[j])
				dist[i]=adjMatrix[j][i]+dist[j];
		}
		
	}
	cout<<dist[n];
	return 0;
}



本题代码如下:

#include
#include 
#include
using namespace std;
#define inf 0xfffffff
int map[110][110],dis[110],visit[110];
int n,m;

int dijstra()
{
    memset(visit,0,sizeof(visit));
    for (int i=1;i<=n;i++)
    {
        dis[i]=map[1][i];  // dis数组存下起点到其他点的距离
    }
    visit[1]=1;
    dis[1]=0;
    for (int i=1;i<=n;i++) // 对n个点遍历
    {
        int pos;
        int min=inf;
        for (int j=1;j<=n;j++)
        {
            if (visit[j]==0&&min>dis[j]) // 找到最小的dis
            {
                pos=j;
                min=dis[j];
            }  /// 找出U集合中路径最短的节点D 加入S集合
        }
        visit[pos]=1;  // 置为已访问
        for (int j=1;j<=n;j++)
        {
        	// 遍历,找出起点到j+j到终点的最短路径
            if (!visit[j]&&dis[j]>dis[pos]+map[pos][j])
            {
                dis[j]=dis[pos]+map[pos][j];  // 更新dis
            }
        }
    }
    return dis[n];
}
int main()
{
    int i,j;
    while(~scanf("%d%d",&n,&m),n||m)  // 输入节点数量和道路数量
    {
        for(i=1;i<=n;++i)
        {
            for(j=1;j<=n;++j)
            {
                map[i][j]=inf;
            }
        }
        int a,b,c;
        for(i=1;i<=m;++i)
        {
            scanf("%d%d%d",&a,&b,&c);  // 起点,终点,权值
            if(c<map[a][b])
            	map[a][b]=map[b][a]=c; // 更新map
        }
        int count=dijstra();
        printf("%d\n",count);
    }
    return 0;
}

最小生成树模板

kruskal法

例题:
测试输入的第1行给出评估的道路条数N,村庄数目M;随后的 N 行,每行给出三个正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。
输入示例

9 14
1 2 4
2 3 8
3 4 7
4 5 9
5 6 10
6 7 2
7 8 1
8 9 7
2 8 11
3 9 2
7 9 6
3 6 4
4 6 14
1 8 8

输出示例:

37

kruskal算法:
加边法,以下图A为起点,手动模拟结果如下所示:
c++写算法题:综合_第6张图片
Kruskal算法的高效实现需要一种称作并查集的结构。
Kruskal算法的过程:

  1. 将全部边按照权值由小到大排序。(使用结构体做邻接矩阵,需要写sort的cmp
  2. 按顺序(边权由小到大的顺序)考虑每条边,只要这条边和我们已经选择的边不构成圈,就保留这条边,否则放弃这条边。(并查集检查find(S)和find(E)是否共一个掌门,若不共则非连通,加入生成树中,且合并S、V
  3. 成功选择(n-1)条边后,形成一棵最小生成树,当然如果算法无法选择出(n-1)条边,则说明原图不连通。

代码模板:

#include 
#include 
#include 
#define MAXN 11  //顶点个数的最大值
#define MAXM 20  //边的个数的最大值
using namespace std;

struct edge  //边
{
    int u, v, w; //边的顶点、权值
}edges[MAXM]; //边的数组

int parent[MAXN];  //parent[i]为顶点 i 所在集合对应的树中的根结点
int n, m;  //顶点个数、边的个数
int i, j;  //循环变量
void UFset( )  //初始化
{
    for( i=1; i<=n; i++ )
        parent[i] = -1;
}
int Find( int x ) //查找并返回节点 x 所属集合的根结点
{
    int s; //查找位置
    for( s=x; parent[s]>=0; s=parent[s] );
    while( s!=x ) //优化方案―压缩路径,使后续的查找操作加速。
    {
        int tmp = parent[x];
        parent[x] = s;
        x = tmp;
    }
    return s;
}

//将两个不同集合的元素进行合并,使两个集合中任两个元素都连通
void Union( int R1, int R2 )
{
    int r1 = Find(R1), r2 = Find(R2); //r1 为 R1 的根结点,r2 为 R2 的根结点
    int tmp = parent[r1] + parent[r2]; //两个集合结点个数之和(负数)
    //如果 R2 所在树结点个数 > R1 所在树结点个数(注意 parent[r1]是负数)
    if( parent[r1] > parent[r2] ) //优化方案――加权法则
    {
        parent[r1] = r2;
        parent[r2] = tmp;
    }
    else
    {
        parent[r2] = r1;
        parent[r1] = tmp;
    }
}
bool cmp( edge a, edge b ) //实现从小到大排序的比较函数
{
    return a.w <= b.w;
}
void Kruskal( )
{
    int sumweight = 0;  //生成树的权值
    int num = 0;  //已选用的边的数目
    int u, v;  //选用边的两个顶点
    UFset( ); //初始化 parent[]数组
    for( i=0; i<m; i++ )
    {
        u = edges[i].u; v = edges[i].v;
        if( Find(u) != Find(v) )  //u和v不是一个掌门,即不连通
        {
            printf( "%d %d %d\n", u, v, edges[i].w );
            sumweight += edges[i].w;
			num++;
            Union(u,v);  // 合并
        }
        if( num>=n-1 ) break;  // 取到n-1个边即结束
    }
    printf( "weight of MST is %d\n", sumweight );
}
int main( )
{
    int u, v, w; //边的起点和终点及权值
    scanf( "%d%d", &n, &m ); //顶点数 n,边数m

    for( int i=0; i<m; i++ )
    {
    	scanf( "%d%d%d", &u, &v, &w ); //读入边的起点和终点,以及权值
    	edges[i].u = u; edges[i].v = v; edges[i].w = w;
    }
    sort(edges,edges+m,cmp);
    Kruskal();
    return 0;
}

dfs综合体题

codevs 1004 四子连棋

c++写算法题:综合_第7张图片
题目链接

此题的难点在于

  1. 模拟棋的行走------------------------------使用空格位置做每次的起点
  2. 模拟黑白轮流执棋-----------------------需要记录上一次执棋的颜色
  3. 有黑白先后手之分------------------------分类深搜取最短的。
#include 
#include 
#include 
#include 
#include 
#define inf 0x3f3f3f3f
using namespace std;
char a[5][5];
int ans,Ox1=0,Oy1,Ox2,Oy2,dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};

bool check() //检查当前棋盘是否构成四子连棋
{
  // 4*4的棋盘,只有行、列、双斜线四种方式
  for(int i=1;i<=4;i++)
  {
    if (a[i][1]==a[i][2]&&a[i][1]==a[i][3]&&a[i][1]==a[i][4]) return 1;
	if (a[1][i]==a[2][i]&&a[1][i]==a[3][i]&&a[1][i]==a[4][i]) return 1;
  }
  if (a[1][1]==a[2][2]&&a[1][1]==a[3][3]&&a[1][1]==a[4][4]) return 1;
  if (a[1][4]==a[2][3]&&a[1][4]==a[3][2]&&a[1][4]==a[4][1]) return 1;
  return 0;
}

bool can(int x,int y,char p)
{
  return x>=1&&x<=4&&y>=1&&y<=4&&a[x][y]!=p;
}

bool dfs(int x1,int y1,int x2,int y2,char pre,int step) //pre表示先手颜色
{
	// dfs(Ox1,Oy1,Ox2,Oy2,'W',0)
  if (step==ans) //搜索深度达到上限,停止
  {
    if (check()) return 1;
	else return 0;
  }
  // 对四个方向遍历
  for(int i=0;i<4;i++)
  {
    int nx1,ny1,nx2,ny2;
	nx1=x1+dir[i][0];
	ny1=y1+dir[i][1];
	nx2=x2+dir[i][0];
	ny2=y2+dir[i][1];
	// 如果当前坐标为有效位置
	if (can(nx1,ny1,pre))  
	{
	  swap(a[x1][y1],a[nx1][ny1]); //交换
	  if (dfs(nx1,ny1,x2,y2,(pre=='B'?'W':'B'),step+1)) return 1; // 轮流
	  swap(a[x1][y1],a[nx1][ny1]);
	}
	if (can(nx2,ny2,pre))
	{
	  swap(a[x2][y2],a[nx2][ny2]);
	  if (dfs(x1,y1,nx2,ny2,(pre=='B'?'W':'B'),step+1)) return 1;
	  swap(a[x2][y2],a[nx2][ny2]);
	}
  }
  return 0;
}

int main()
{
	// 读取棋盘
  for(int i=1;i<=4;i++)
  {
    char s[5];
    scanf("%s",s);
	for(int j=1;j<=4;j++)
	{
	  a[i][j]=s[j-1];
	  // 分别记录下O的位置
      if (a[i][j]=='O')
	  {
	    if (Ox1==0)
			Ox1=i,Oy1=j;
		else
			Ox2=i,Oy2=j;
	  }
	}
  }

  for(ans=1;ans<=inf;ans++) //ans枚举深度上限
  {
    if (dfs(Ox1,Oy1,Ox2,Oy2,'W',0)) break; //黑先手
	if (dfs(Ox1,Oy1,Ox2,Oy2,'B',0)) break; //白先手
  }

  printf("%d",ans);

  return 0;
}

dp动态规划

codevs 1010 过河卒

c++写算法题:综合_第8张图片
题目链接

思路:典型的dp,先置马的控制点为1,令其他点均为0,0代表当前路可走,此处在处理的时候不需要判断边界条件,主要此处的巧妙使用。

//刷表法
#include
using namespace std;
int n, m, x, y, a[20][20], dp[20][20];
int main(){
    cin>>n>>m>>x>>y;  //行,列,马的坐标
    a[x][y] = 1;
    a[x-1][y-2] = a[x-1][y+2] = a[x+1][y-2] = a[x+1][y+2] = 1; // 躺 "日"
    a[x-2][y-1] = a[x-2][y+1] = a[x+2][y-1] = a[x+2][y+1] = 1; //正 "日"
    dp[0][0] = 1;	//初始值, 刷表时不会覆盖所以可以直接放
    
    for(int i = 0; i <= n; i++){
        for(int j = 0; j <= m; j++){
        	// 0位安全点,可走
            if(a[i+1][j]==0)  // 右移安全,则右移点到达次数+当前
				dp[i+1][j] += dp[i][j];
            if(a[i][j+1]==0)
				dp[i][j+1] += dp[i][j];
        }
    }
    cout<<dp[n][m]<<"\n";
    return 0;
}

codevs 1014 装箱问题

c++写算法题:综合_第9张图片
题目链接
背包问题,注意此处的巧解:

#include 
#include 
using namespace std;
int dp[20005]={0};
int main()
{
	int n,v;
	cin>>v>>n; //输入体积和物品数
	int volume;
	for(int i=1;i<=n;i++)
	{
		cin>>volume; // 物品体积
		for(int j=v;j>=volume;j--) //j为当前箱子的剩余体积,要比物体体积大才操作
		{
			//dp[j]为箱子剩余体积为j时所能装入的最大体积
			dp[j]=max(dp[j],dp[j-volume]+volume); //
		}
	}
	cout<<v-dp[v]<<endl;
	return 0;
}

Floyd多源最短路

codevs 1020 孪生蜘蛛

c++写算法题:综合_第10张图片
题目链接

#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int n,t,a,b;
int x,y,maxn = -1,ans = INF;
int num[MAXN][MAXN];

int main()
{
    scanf("%d",&n);
    memset(num,1,sizeof(num));
    for(int i = 1; i <= n; i ++)
        num[i][i] = 0;
    while(scanf("%d%d%d",&x,&y,&t) == 3)
        num[x][y] = num[y][x] = t;
    // floyd算法,多源最短路
    for(int k = 1; k <= n;k ++)
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                num[i][j] = min(num[i][j],num[i][k] + num[k][j]);

    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
        {
            maxn = -1;
            for(int k = 1; k <= n; k ++)
                maxn = max(min(num[i][k],num[k][j]),maxn); // 最短的里面取出来最大的
            if(maxn < ans && i != j)
                ans = maxn,x = i,y = j;
        }
    printf("%d %d\n",x,y);
    return 0;
}

最小生成树

prim算法

c++写算法题:综合_第11张图片

codevs 1003 电话连线

给出邻接矩阵,要求求最小生成树的值。链接
c++写算法题:综合_第12张图片
注意这里的dist矩阵并不是某起点到其余点的距离,而是集合U到集合V的点的较小距离
因而不像kruskal算法那样更新距离:

	if (!visit[j]&&dis[j]>dis[pos]+map[pos][j])  {
         dis[j]=dis[pos]+map[pos][j];  // 更新dis
    }
#include
#include
#define inf 99999999
int map[1010][1010],dis[1010],book[1010];
int main()
{
	int n,a,b,c,i,j,k,min;
	int count,sum;
	while(scanf("%d",&n)!=EOF)  // 输入结点数 
	{
		count=sum=0;
		memset(book,0,sizeof(book));
		// 初始化邻接矩阵 
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
			{
				if(i==j)
					map[i][j]=0;
				else
					map[i][j]=inf;
			}
		// 邻接矩阵赋值 
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
			{
				scanf("%d",&a);
				if(a<map[i][j])
				{
					map[i][j]=a;
					map[j][i]=a;
				}
			}
		// 记录第一个点到其他点的距离 
		for(i=1;i<=n;i++)
			dis[i]=map[1][i];
		book[1]=1; // 访问数组 
		count=1;
		// 思路: 
		/*初始化集合U只包含起点,V包含其余所有点。 从起点开始,找离当前点最近的点加入集合U中,
		并从集合V中移除,标记已访问; 重复以上步骤,直至集合V空 
		*/ 
		while(count<n)
		{
			min=inf;
			for(i=1;i<=n;i++) // 对n个点遍历 ,找出最近的点 
			{
				if(book[i]==0&&dis[i]<min)  // 若该点未访问,且距离更小则更新 
				{ 
					min=dis[i];  // 更新距离 
					j=i;   // 记录最近的点的位置 
				}
			}
			book[j]=1;  // 移除该点,即标记为已访问 
			count++;
			sum+=dis[j];
			for(k=1;k<=n;k++)
			{
				if(book[k]==0&&dis[k]>map[j][k])  // 该点未访问过,且距离更小则更新 
					dis[k]=map[j][k];  // 更新距离 
			}
		}
		printf("%d\n",sum);  // 输出最小生成树的值 
	}
	return 0;
}

最小生成树二刷

题目链接

**

注意,数组一定要初始化,因为

**
c++写算法题:综合_第13张图片
先将prim算法模板总结如下:

最小生成树prim算法步骤

1.初始化邻接矩阵,对角线全0,连接不到则为inf

2.把起点加入集合U
3.for i 1 -> n :  // 遍历剩下n-1个点
    for j 0 -> n : // 遍历集合V,寻找最近的点
	
	寻找集合V中点dist[]最小的值,记为min,并标记该点index
    
    把最小点加入集合U中,累加该距离
    
    for k 0 -> n :  // 遍历集合U,更新集合U到集合V的距离
    
		dist[k]=min(dist[k],map[index][k] );             // dist[i]代表未访问路径中,集合U和集合V中距离最小的两点距离
	
#include
#include
using namespace std; 
// 11.30
int map[100][100]; 
// 最小生成树问题 
int main(){
	// 输入结点数n
	int n;
	cin>>n;
	// 输入邻接矩阵map
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cin>>map[i][j];
		}
	}
	// 创建dist矩阵,dist为当前 
	int dist[100];
	for(int i=0;i<n;i++){
		dist[i]=map[0][i];
	}
	// prim算法实现---加点法,其实是n个点里面不断加入边 
	/* 对于某一点,选定起点a后(集合U),遍历集合V,找到最近的b点加入
	 标记点b为已经访问(加入集合U),更新dist矩阵,dist矩阵为起点到其他点的最近距离;
	 遍历集合V,找到集合V中离集合U内的点最近的距离点c,加入集合U中,再更新dist矩阵 
	  
	*/ 
	int vis[100];
	for(int i=0;i<n;i++)
		vis[i]=0;
	vis[0]=1;
	int sum=0; 
	for(int i=0;i<n-1;i++){  // 对n个点遍历,
		int min=0x3f3f3f3f;
		int index=0; 
		for(int j=0;j<n;j++){ // 对集合V遍历 ,找到集合V中最近的且未访问过的点 
			if(vis[j]==0&&dist[j]<min){
				min=dist[j];
				index=j;		
			}
		}
		vis[index]=1; // 加入集合U
		sum+=dist[index];
		// 更新dist 
		for(int k=0;k<n;k++){
			if(vis[k]==0&&dist[k]>map[index][k])
				dist[k]=map[index][k];			
		}
			
	} 
		
	
	cout<<sum; 
}

动态规划

典型求大数余类型

935. Knight Dialer

c++写算法题:综合_第14张图片
注意对于大数余法,我们有定律:(a+b)%c=((a%c)+(b%c))%c,因而我们常用以下形式来计算:

int add(int a, int b) { return (a + b) % MOD;} 

因而以下代码结果是一样的

#include 

using namespace std;

int MOD;
int add(int a, int b) { return (a + b) % MOD;}
int main(){
	int a,b;
	while(cin>>a>>b>>MOD)
		cout<

代码如下:

class Solution {
    const int MOD = 1e9+7;
    int add(int a, int b) { return (a + b) % MOD; }
public:
    int knightDialer(int N) {
        int dp[5111][10]; // dp[i][j]表示走i步,当前处于第j位的走法有多少种
        for(int i=0;i<10;i++) 
            dp[0][i] = 1;
        for(int t=1;t<N;t++) {
            dp[t][0] = add(dp[t-1][4], dp[t-1][6]);
            dp[t][1] = add(dp[t-1][6], dp[t-1][8]);
            dp[t][2] = add(dp[t-1][7], dp[t-1][9]);
            dp[t][3] = add(dp[t-1][4], dp[t-1][8]);
            dp[t][4] = add(dp[t-1][0], add(dp[t-1][9], dp[t-1][3]));
            dp[t][5] = 0;
            dp[t][6] = add(dp[t-1][1], add(dp[t-1][7], dp[t-1][0]));
            dp[t][7] = add(dp[t-1][2], dp[t-1][6]);
            dp[t][8] = add(dp[t-1][1], dp[t-1][3]);
            dp[t][9] = add(dp[t-1][2], dp[t-1][4]);
        }
        
        int ans = 0;
        for(int i=0;i<10;i++) 
            ans = add(ans, dp[N-1][i]);
        return ans;
    }
};

你可能感兴趣的:(算法笔记)