九月十九号:

博客介绍了旅行商、八皇后、迷宫等算法经典问题。八皇后问题是回溯算法典型案例,可用图论方法求解。迷宫问题涉及深搜和广搜。还解析了Codeforces比赛中的题目,如找mex为k的n个数的和最大值、求数组异或和的最值等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.旅行商问题:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxx=105;
const int inf=0x3f3f3f3f;
int g[maxx][maxx];
int x[maxx];
int bestx[maxx];
int bestl;
int cl;
int n,m;
void init(){
	memset(bestx,0,sizeof(bestx));
	memset(g,0,sizeof(g));
	memset(x,0,sizeof(x));
	cl=0;bestl=inf;
	for(int i=1;i<=n;i++){
		x[i]=i;
		bestx[i]=i;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j){
				g[i][j]=0;
			}else{
				g[i][j]=inf;
			}
		}
	}
}
void Traveling(int t){
	if(t>n){//到达叶子节点 
	//推销货物的最后一个城市与住地城市有连边相连并且路径长度比当前最优值小,说明找到一条 
	//更好的路径,记录相关信息 
		if(g[x[n]][1]!=inf&&(cl+g[x[n]][1]<bestl)){
			for(int i=1;i<=n;i++){
				bestx[i]=x[i];
			}
			bestl=cl+g[x[n]][1];
		}
	}else{//没有到达叶子节点 
		for(int j=t;j<=n;j++){//搜索扩展节点的所有分支 
		//如果t-1个城市与第t个城市有边相连并且可能得到更短的路线 
			if(g[x[t-1]][x[j]]!=inf&&(cl+g[x[t-1]][x[j]]<bestl)){
				//保存第t个要去的城市编号到x[t]中,进入到第t+1层 
				swap(x[t],x[j]);//交换两个元素的值 
				cl+=g[x[t-1]][x[t]];
				Traveling(t+1);//从第t+1层的扩展节点继续搜索 
				//第t+1层搜索完毕,回溯到第t层 
				cl-=g[x[t-1]][x[t]];
				swap(x[t],x[j]);
			}
		}
	}
}
int main(){
	while(scanf("%d %d",&n,&m)!=EOF){
		init();
		for(int i=1;i<=m;i++){
			int a,b,cost;
			scanf("%d %d %d",&a,&b,&cost);
			g[a][b]=g[b][a]=cost;
		}
		Traveling(2);
		cout<<"最短的距离: "<<bestl<<endl;
		cout<<"最短的路径: ";
		for(int i=1;i<=n;i++){
			cout<<bestx[i]<<" ";
		}
		cout<<endl;
	}
	return 0;
} 

/*
5 9
1 2 10
1 5 12
1 4 4
2 5 5
5 4 6
2 4 8
5 3 30
2 3 15
4 3 7
*/

2八皇后问题:

八皇后问题(英文:Eight queens),是由国际象棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。

问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。
 

    #include<iostream>
    using namespace std;
    int cnt = 1;//方案计数器
    int chess[8][8];//棋盘情况
    bool judge(int row, int col);//判断某个位置是否可放
    void print();//输出函数
    void dfs(int row) {
    	if (row >= 8) {//深搜结束条件
    		print();
    		cnt++;
    		return;
    	}
    	for (int j = 0; j <= 7; j++) {
    		if (judge(row, j)) {
    			chess[row][j] = 1;
    			dfs(row + 1);//深搜
    			chess[row][j] = 0;//回溯,表示换个位置试试,之前的位置就当作没走咯
    		}
    	}
    }
    int main() {
    	dfs(0);
    	return 0;
    }
    bool judge(int row, int col) {
    	for (int i = 0; i <= 7; i++)//判断列
    		if (chess[i][col] == 1) return false;
    	for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {//判断左上对角线
    		if (chess[i][j] == 1) return false;
    	}
    	for (int i = row, j = col; i >= 0 && j <= 7; i--, j++) {//判断右上对角线
    		if (chess[i][j] == 1) return false;
    	}
    	return true;
    }
    void print() {
    	cout << "No. " << cnt << endl;
    	for (int j = 0; j <= 7; j++) {//!!!按列输出!!!
    		for (int i = 0; i <= 7; i++) {
    			if (chess[i][j] == 1) cout << "1" << " ";
    			else cout << "0" << " ";
    		}
    		cout << endl;
    	}
    }

3迷宫问题,并且是记录路径。

深搜:

#include <iostream>
using namespace std;

int maze[8][8] = {{0,0,0,0,0,0,0,0},{0,1,1,1,1,0,1,0},{0,0,0,0,1,0,1,0},{0,1,0,0,0,0,1,0},
{0,1,0,1,1,0,1,0},{0,1,0,0,0,0,1,1},{0,1,0,0,1,0,0,0},{0,1,1,1,1,1,1,0}};
//下、右、上、左
const int fx[4] = {1,0,-1,0};
const int fy[4] = {0,1,0,-1};
//maze[i][j] = 3;//标识已走
//maze[i][j] = 2;//标识死胡同
//maze[i][j] = 1;//标识墙
//maze[i][j] = 0;//标识可以走

//打印路径
void printPath()
{
    for (int i=0;i<8;++i)
    {
        for (int j=0;j<8;++j)
        {
            if (3 == maze[i][j])
            {
                cout<<"V";
            }
            else
            {
                cout<<"*";
            }
        }
        cout<<endl;
    }
    cout<<endl;
}

void search(int i, int j)
{
    int newx;
    int newy;
    for (int k=0;k<4;++k)
    {
        newx = i+fx[k];
        newy = j+fy[k];
        //如果不是墙,且没有走过
        if (newx>=0 && newx <8 && newy>=0 && newy<8 && 0 == maze[newx][newy])
        {
            maze[newx][newy] = 3;
            if (7 == newx && 7 == newy)
            {
                printPath();
                maze[newx][newy] = 0;
            }
            else
            {
                search(newx,newy);
            }       
        }
    }
    **//回溯的时候将此点标记未访问,这样下一条路径也可以访问**
    maze[i][j] = 0;
}

int main()
{
    maze[0][0] = 3;
    search(0,0);
    return 0;
}

广搜:

#include <iostream>
#include <vector>
using namespace std;

int maze[8][8] = {{0,0,0,0,0,0,0,0},{0,1,1,1,1,0,1,0},{0,0,0,0,1,0,1,0},{0,1,0,0,0,0,1,0},
{0,1,0,1,1,0,1,0},{0,1,0,0,0,0,1,1},{0,1,0,0,1,0,0,0},{0,1,1,1,1,1,1,0}};
//下、右、上、左
const int fx[4] = {1,0,-1,0};
const int fy[4] = {0,1,0,-1};

struct sq{
    int x;
    int y;
    int pre;
};

//标记路径,正确的点值赋为3
void markPath(const vector<sq> &q, int index)
{
    sq tmp = q[index];
    maze[tmp.x][tmp.y] = 3;
    if ( 0 == tmp.x && 0 == tmp.y )
    {
        return ;
    }
    else
    {
        markPath(q, tmp.pre);
    }
}

//打印路径
void printPath()
{
    for (int i=0;i<8;++i)
    {
        for (int j=0;j<8;++j)
        {
            if (3 == maze[i][j])
            {
                cout<<"v";
            }
            else
            {
                cout<<"*";
            }
        }
        cout<<endl;
    }
    cout<<endl;
}

//检查点(i,j)是否满足
bool check(int i, int j)
{
    if (i >= 0 && i<8 && j>=0 && j<8 && 0 == maze[i][j])
    {
        return true;
    }
    return false;
}

void search()
{

    //模仿队列,之所以不用真正的队列,因为后面需要通过下标对队列进行随机访问
    vector<sq> q;
    int qh = 0;
    sq fnode;
    fnode.pre = -1;
    fnode.x = 0;
    fnode.y = 0;
    //标记已访问
    maze[fnode.x][fnode.y] = -1;
    q.push_back(fnode);
    int qe = 1;
    sq tmp;
    while (qh != qe)
    {
        //出队
        tmp = q[qh];
        ++qh;
        int newx, newy;
        for (int k=0;k<4;++k)
        {
            newx = tmp.x + fx[k];
            newy = tmp.y + fy[k];
            if (check(newx, newy))
            {
                sq n_node;
                n_node.pre = qh - 1;
                n_node.x = newx;
                n_node.y = newy;
                //入队
                q.push_back(n_node);
                ++qe;
                //找到出口,打印
                if (7 == newx && 7 == newy)
                {
                    markPath(q, qe-1);
                    printPath();
                    return;
                }
            }
        }
        //maze[tmp.x][tmp.y] = -1;
    }
}

int main()
{
    maze[0][0] = 3;
    search();
    return 0;
}

4Dashboard - CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!) - Codeforces

A:

.让你找mex为k的n个数,这n个数从0-x,问n个数的和最大值是多少

先判断不行的。然后行的肯定有0-k-1,剩下还有就选x就行。


#include<iostream>
using namespace std;
typedef long long ll;
void solve()
{
	int n,k,x;
	cin>>n>>k>>x;
	if(n<k||x<k-1)
	{
		cout<<-1<<'\n';
		return;
	}
	int ans=k*(k-1)/2;
	int lef=n-(k);
	if(x>k)ans+=lef*x;
	else ans+=lef*(k-1);
	cout<<ans<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--)solve();
	return 0;
}

B:给n个元素数组a和m个元素数组b,每次你可以任选一个bj元素,让ai=ai|bj,若干次操作,问a数组的异或和最小和最大分别是多少。

#include <bits/stdc++.h>
using namespace std;

int n,cnt;
void TT(int n,int i){
	if(i>=n) return;
	for(int j=i;j*j<=n;j++){
		if(n%j==0){
			cnt++;
			TT(n/j,j);
		}
	}
	
	
}

int main()
{

 
    int T;
    cin >> T;
    while(T--){
        int n, m;
        cin >> n >> m;
        vector<int> a(n), b(m);
        for(int i = 0; i < n; i++) cin >> a[i];
        int s = 0;
        for(int i = 0; i < m; i++)
            cin >> b[i], s |= b[i];
        if (n % 2 == 0){
            int mn = 0, mx = 0;
            for(auto x : a){
                mx ^= x;
                mn ^= (x | s);
            }
            cout << mn << ' ' << mx << '\n';
        }
        else{
            int mn = 0, mx = 0;
            for(auto x : a){
                mx ^= (x | s);
                mn ^= x;
            }
            cout << mn << ' ' << mx << '\n';
        }
    }
}
    
 

C:

根据样例可以发现,最终每一位答案必然是最左边和最右边能出现的最远距离,这里需要跟新的就是可能前一位可以跟新其他的最大值和最小值

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
const int N=2e6+10,M=2e5;
typedef double db;
typedef pair<int,int>pii;
int n,m,k,Q,cnt,t,x;
vector<int>del;
int a[200010],b[200010];
int prime[N];
bool st[N];
map<int,int>mp;
int nm[36][2],mle[36][2];
int l[N],r[N];
void solve(){
   cin>>n>>k;
   rep(i,1,k)l[i]=N,r[i]=0;
   rep(i,1,n)cin>>a[i];
   mp.clear();
   rep(i,1,n){
       l[a[i]]=min(l[a[i]],i),r[a[i]]=max(r[a[i]],i);
       mp[a[i]]++;
   }
   per(i,0,k-1){
       l[i]=min(l[i],l[i+1]);
       r[i]=max(r[i],r[i+1]);
   }
   rep(i,1,k){
       if(mp[i]){
           cout<<(r[i]-l[i]+1)*2<<" ";
       }else{
           cout<<0<<" ";
       }
   }
   cout<<endl;
   
}
signed main(){
    cin>>t;
    while(t--)solve();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值