c++寫算法題:綜合

拯救公主

百鍊OJ, 題目鏈接
在這裏插入圖片描述
在這裏插入圖片描述
思路,典型的bfs問題,但需要處理的條件較多,此種題適合使用結構體來做,注意熟練使用結構體編程。
先貼上代碼,以後再多寫幾遍,這裏用到了狀態壓縮的方法記錄寶石的數量。

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
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 <iostream>
#include <unordered_map>
#include <vector>
#include <queue>
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<vector<char>> a(R,vector<char>(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<Node*> 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:仙島求藥

在這裏插入圖片描述
題目鏈接

#include<stdio.h>
#include <iostream>
#include<string.h>
#include <queue>
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<<newNode->x<<"  "<<newNode->y<<endl;
				}
			}
		}
		
	}
	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模板題

最短路

題目鏈接

在這裏插入圖片描述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

在這裏插入圖片描述
單源最短路模板

#include<stdio.h>
#include <iostream>
#include<string.h>
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<stdio.h>
#include <iostream>
#include<string.h>
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爲起點,手動模擬結果如下所示:
在這裏插入圖片描述
Kruskal算法的高效實現需要一種稱作並查集的結構。
Kruskal算法的過程:

  1. 將全部邊按照權值由小到大排序。(使用結構體做鄰接矩陣,需要寫sort的cmp
  2. 按順序(邊權由小到大的順序)考慮每條邊,只要這條邊和我們已經選擇的邊不構成圈,就保留這條邊,否則放棄這條邊。(並查集檢查find(S)和find(E)是否共一個掌門,若不共則非連通,加入生成樹中,且合併S、V
  3. 成功選擇(n-1)條邊後,形成一棵最小生成樹,當然如果算法無法選擇出(n-1)條邊,則說明原圖不連通。

代碼模板:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#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 四子連棋

在這裏插入圖片描述
題目鏈接

此題的難點在於

  1. 模擬棋的行走------------------------------使用空格位置做每次的起點
  2. 模擬黑白輪流執棋-----------------------需要記錄上一次執棋的顏色
  3. 有黑白先後手之分------------------------分類深搜取最短的。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#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 過河卒

在這裏插入圖片描述
題目鏈接

思路:典型的dp,先置馬的控制點爲1,令其他點均爲0,0代表當前路可走,此處在處理的時候不需要判斷邊界條件,主要此處的巧妙使用。

//刷表法
#include<iostream>
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 裝箱問題

在這裏插入圖片描述
題目鏈接
揹包問題,注意此處的巧解:

#include <iostream>
#include <algorithm>
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 孿生蜘蛛

在這裏插入圖片描述
題目鏈接

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
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算法

在這裏插入圖片描述

codevs 1003 電話連線

給出鄰接矩陣,要求求最小生成樹的值。鏈接
在這裏插入圖片描述
注意這裏的dist矩陣並不是某起點到其餘點的距離,而是集合U到集合V的點的較小距離
因而不像kruskal算法那樣更新距離:

	if (!visit[j]&&dis[j]>dis[pos]+map[pos][j])  {
         dis[j]=dis[pos]+map[pos][j];  // 更新dis
    }
#include<stdio.h>
#include<string.h>
#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;
}

最小生成樹二刷

題目鏈接

**

注意,數組一定要初始化,因爲

**
在這裏插入圖片描述
先將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<iostream>
#include<cstdio>
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

在這裏插入圖片描述
注意對於大數餘法,我們有定律:(a+b)%c=((a%c)+(b%c))%c,因而我們常用以下形式來計算:

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

因而以下代碼結果是一樣的

#include <iostream>

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<<add(a,b)<<"  "<<(a+b)%MOD<<endl; 
	return 0;
}

代碼如下:

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;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章