codeforces 152E bfs+状态压缩

7 篇文章 0 订阅
3 篇文章 0 订阅
/*题意:在一些n*m的网格中  每个格子里面都有一些不同数量的花 问现在要使得一些给定的重要位置联通 你需要破坏至少多少花来使得道路联通*/
/* 我们采用暴力的思维 把重要位置当成状态来处理 然后枚举出发的位置点 取最优结果就ok啦*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<sstream>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
using namespace std;
typedef long long ll;
#define inf 1<<29
#define eps 1e-10
#define maxl 210
#define mem(i,j) memset(i,j,sizeof(i))
int dp[maxl][1<<7],pre[maxl][1<<7];//状态压缩  dp表示从网格中某个格子出发访问过某几个所需要的最小花费
int n,m,k,nn,mm;
int hash1[maxl];
int maz[maxl][maxl];//初始值
char g[maxl][maxl];//保存结果
bool visit[maxl][1<<7];
int dx[]= {0,0,-1,1};
int dy[]= {-1,1,0,0};
struct Node
{
    int u,st;
    Node(int _u,int _st)
    {
        u=_u,st=_st;
    }
};

queue<Node> que;

bool check(int x,int y)
{
    if(x>=0&&x<n&&y>=0&&y<m) return true;
    return false;
}

void update(int u,int st,int w,int fa)
{
    if(dp[u][st]>w)//累计和
    {
        dp[u][st]=w;
        pre[u][st]=fa;
        if(!visit[u][st])
        {
            que.push(Node(u,st));
            visit[u][st]=true;
        }
    }
}

void dfs(int u,int st)
{
    int x=u/m,y=u%m;
    g[x][y]='X';
    if(pre[u][st]==-1) return ;
    else
    {
        int v=pre[u][st]/1000,stt=pre[u][st]%1000;
        dfs(v,stt);
        if(stt-st) dfs(v,st-stt);
    }
}

void solve()
{
    while(!que.empty())
    {
        Node now=que.front(); que.pop();
        int u=now.u,x=now.u/m,y=now.u%m,st=now.st;//取出得到对应行列
        visit[u][st]=false;
        for(int i=0; i<4; i++)
        {
            int xx=x+dx[i],yy=dy[i]+y;
            if(!check(xx,yy)) continue;
            int v=xx*m+yy;
            update(v,st,dp[u][st]+maz[xx][yy],u*1000+st);//向四周扩展
        }
        //这部分是最关键的
        int t=mm-1-st;
        for(int i=t; i; i=(i-1)&t)//i指的就是取另外与st无交集的另外一些
        {
            update(u,i|st,dp[u][i]+dp[u][st]-maz[x][y],u*1000+st);//注意这里减去重复的部分  因为是从st 这个点的位置出发走了俩次 所以减去
        }
    }

    int ans=inf,u;
    for(int i=0; i<nn; i++)
        if(ans>dp[i][mm-1])//mm-1表示全部访问完成  看从图上的那个格子出发取的最小
        {
            ans=dp[i][mm-1];
            u=i;
        }
    dfs(u,mm-1);
    cout<<ans<<endl;
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<m; j++)
            cout<<g[i][j];
        cout<<endl;
    }
}
int main()
{
    freopen("in.txt", "r", stdin);
    while(cin>>n>>m>>k)
    {
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
            {
                cin>>maz[i][j];
                g[i][j]='.';
            }
        nn=n*m;
        mm=1<<k;
        memset(hash1,0,sizeof(hash1));
        memset(visit,false,sizeof(visit));
        for(int i=0; i<nn; i++)
            for(int j=0; j<mm; j++)
                dp[i][j]=inf;

        for(int i=0,a,b; i<k; i++)
        {
            cin>>a>>b;
            a--,b--;
            int u=a*m+b;
            hash1[u]=1<<i;
            update(u,hash1[u],maz[a][b],-1);
        }
        solve();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值