Codeforces Round #108 (Div. 2)——状态压缩DP+spfa+dfs——Garden

Vasya has a very beautiful country garden that can be represented as an n × m rectangular field divided into n·m squares. One beautiful day Vasya remembered that he needs to pave roads between k important squares that contain buildings. To pave a road, he can cover some squares of his garden with concrete.

For each garden square we know number aij that represents the number of flowers that grow in the square with coordinates (i, j). When a square is covered with concrete, all flowers that grow in the square die.

Vasya wants to cover some squares with concrete so that the following conditions were fulfilled:

  • all k important squares should necessarily be covered with concrete
  • from each important square there should be a way to any other important square. The way should go be paved with concrete-covered squares considering that neighboring squares are squares that have a common side
  • the total number of dead plants should be minimum

As Vasya has a rather large garden, he asks you to help him.

Input

The first input line contains three integers nm and k (1 ≤ n, m ≤ 100, n·m ≤ 200, 1 ≤ k ≤ min(n·m, 7)) — the garden‘s sizes and the number of the important squares. Each of the next n lines contains m numbers aij (1 ≤ aij ≤ 1000) — the numbers of flowers in the squares. Next k lines contain coordinates of important squares written as "x y" (without quotes) (1 ≤ x ≤ n, 1 ≤ y ≤ m). The numbers written on one line are separated by spaces. It is guaranteed that all k important squares have different coordinates.

Output

In the first line print the single integer — the minimum number of plants that die during the road construction. Then print n lines each containing m characters — the garden‘s plan. In this plan use character "X" (uppercase Latin letter X) to represent a concrete-covered square and use character "." (dot) for a square that isn‘t covered with concrete. If there are multiple solutions, print any of them.

Sample test(s)

input

3 3 21 2 31 2 31 2 31 23 3

output

9.X..X..XX

input

4 5 41 4 5 1 22 2 2 2 72 4 1 4 53 2 1 7 11 11 54 14 4

output

26X..XXXXXX.X.X..X.XX.原博文:http://blog.csdn.net/gzh1992n/article/details/9119543 不知道最小生成树能不能搞出来
/*
题意:把k个点连起来权值最小,并且输出图
令dp[i][j] 为当前为第i个点,已经连了j状态所需连得点的最小已连接权值
所以就有两个动态转移方程
1:根据边  dp[i][j] = min(dp[k][j] + maze[k][i])  相同的状态,连接点转移
2:根据状态 dp[i][j] = min(dp[i][j1] + dp[i][j2] - maze[i/m][i%m]) j1,j2为j的子集
dfs找路径
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#include <string>
#include <map>
using namespace std;
const int inf = 1 << 29;
const int MAX = 200 + 10;
int dp[MAX][1<<7], pre[MAX][1<<7];
int n, m, k, nn, mm;
int hash1[MAX];
int maz[MAX][MAX];
char g[MAX][MAX];
bool visit[MAX][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;
}

//update(u, hash[u], maz[a][b], -1);
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() {//spfa
    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 = y + dy[i];
            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) {
            update(u, i | st, dp[u][i] + dp[u][st] - maz[x][y], u * 1000 + st);//状态的动态方程,因为是同一个点要减去一个重
        }
    }
    int ans = inf, u;
    for(int i = 0; i < nn; i++) {
        if(ans > dp[i][mm-1]) {
            ans = dp[i][mm-1];
            u = i;
        }
    }//找点中最小值
    dfs(u, mm - 1);//DFS找路径
    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() {
    while(cin >> n >> m >> k) {
        while(!que.empty()) que.pop();
        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();
    }
    return 0;
}

  

时间: 2024-08-08 16:26:40

Codeforces Round #108 (Div. 2)——状态压缩DP+spfa+dfs——Garden的相关文章

组合数学题 Codeforces Round #108 (Div. 2) C. Pocket Book

题目传送门 1 /* 2 题意:每一次任选i,j行字符串进行任意长度前缀交换,然后不断重复这个过程,问在过程中,第一行字符串不同的个数 3 组合数学题:每一列不同的字母都有可能到第一行,所以每列的可能值相乘取模就行了.这题主要坑在题意理解上... 4 */ 5 #include <cstdio> 6 #include <algorithm> 7 #include <cstring> 8 #include <cmath> 9 #include <map&

Codeforces Round #359 (Div. 2) C. Robbers&#39; watch (暴力DFS)

题目链接:http://codeforces.com/problemset/problem/686/C 给你n和m,问你有多少对(a, b) 满足0<=a <n 且 0 <=b < m 且a的7进制和n-1的7进制位数相同 且b的7进制和m-1的7进制位数相同,还有a和b的7进制上的每位上的数各不相同. 看懂题目,就很简单了,先判断a和b的7进制位数是否超过7,不超过的话就dfs暴力枚举计算就可以了. 1 //#pragma comment(linker, "/STACK

HDU 4085 Peach Blossom Spring 斯坦纳树 状态压缩DP+SPFA

状态压缩dp+spfa解斯坦纳树 枚举子树的形态 dp[i][j] = min(dp[i][j], dp[i][k]+dp[i][l]) 其中k和l是对j的一个划分 按照边进行松弛 dp[i][j] = min(dp[i][j], dp[i'][j]+w[i][j])其中i和i'之间有边相连 #include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn

HDU3768 Shopping(状态压缩DP+spfa)旅行商问题

Shopping Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 577    Accepted Submission(s): 197 Problem Description You have just moved into a new apartment and have a long list of items you need

Codeforces Round #168 (Div. 1)B 树形dp

//给一棵树,每次操作可以将包括顶点1的连通子集的所有点的节点加1或减1 //问最少几次操作使得这棵树的所有顶点的值都为0 //以1为根节点建树 //将加和减分开考虑,用up[u],down[u]表示以u为跟节点的子树中需要加的操作 //最大为up[u] ,需要减的操作最大为down[u] //其余的加和减的操作则可以在处理这两个操作时一起覆盖 //在将u的子数全都处理完后u点的值由于在加了up[u]和减了down[u]后变为 //一个新的值,则对于这个新的值根据它的正负并入up[u]或down

Codeforces Round #135 (Div. 2) D 树形dp

//任选一个根节点,用dfs搜两遍,第一遍找以该节点为根节点 //每一个节点的子树中的相反的边 //第二遍dfs以同样的根节点搜索一遍,记录从根节点到该节点 //正向边和反向边dp[u] = dp[root] - sum_0 + sum_1 : #include<cstdio> #include<cstring> #include<iostream> using namespace std ; const int maxn = 200010 ; const int in

Codeforces Round #148 (Div. 1)C 树形dp

//枚举所有边,把该树分为两个树,分别求两颗数的最小的改变量 #include<cstdio> #include<cstring> #include<iostream> using namespace std ; const int maxn = 3030 ; const int inf = 0x7fffffff ; int dp[maxn] ; struct Edge { int flag , v; int u ; int next ; }edge[2*maxn] ;

Codeforces Round #316 (Div. 2) D. Tree Requests(DFS+状态压缩)

题意:给定一棵树,n个节点.每一个节点处有一个字母,结点的深度定义为节点到根结点1的距离, 有m个询问(u.v),每次回答以结点u为根的子树的深度为v的那些节点处的字母是否能组成一个回文串,特别的,空串也是回文串. 思路:首先说明推断回文串的方法,仅仅要出现次数为奇数个字母个数不超过2.那么这些字母一定能够组成回文串. 接下来考虑将树转成线性结构. 利用dfs+时间戳将结点依照深度存入一个线性结构里,Depth[i]数组里存的是深度为i的全部结点, 那么对于询问有三种情况.一种是dep[u]>=

Codeforces 580D Kefa and Dishes(状态压缩DP)

题目链接:http://codeforces.com/problemset/problem/580/D 题目大意:有n盘菜每个菜都有一个满意度,k个规则,每个规则由x y c组成,表示如果再y之前吃x那么满意度会额外增加c,现在凯迪想吃m盘菜,并且满意度最大,请求出满意度.解题思路:状压DP,设dp[i][j]表示在状态i并且最后一道菜放在位置j时的最大满意度.注意要处理好一道菜时的情况,以及注意二进制表示中1的个数超过m的情况. 代码: 1 #include<bits/stdc++.h> 2