【状压DP】【cofun1375】麦田

博客讲述了如何使用状压DP解决曹操在麦田中设立埋伏点的问题,以最小化破坏的麦苗数量。曹操要求设立k个埋伏点,使得每个点间都有道路相通,并且最大化剩余麦苗。输入包括麦田的尺寸和埋伏点位置,输出为最多能剩余的麦苗数。样例输入和输出展示了具体的应用情况,以及转移方程的分析。

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

【cofun1375】麦田

Description
话说公元199年,曹操准备与袁绍进行官渡决战。为整肃军纪,曹操下令:“全军将士,上至统帅,下至马佚,行军训练,不准践踏庄稼,不准倒犯民利,违令者斩首。”遇有麦场,骑兵下马,扶麦而行。百姓见状,交口称赞。

不巧,在一次出巡时,曹操乘坐的战马受惊,跃入麦田,践踏了一片麦苗。曹操当即下马,请求主簿依军令将自己斩首示众。在众将领劝说下,割发代首,以儆效尤,此事才算了结。

然而,为了赢得官渡之战,曹操又遇到了麻烦。为了对袁绍的一路军队进行埋伏,需要在麦田中设立几个埋伏点,而且几个点之间,还需要有道路相通。埋伏点和道路均会将所在格子麦苗破坏掉。这可难为了刚刚割发代首的曹孟德。经过重重的抉择,曹操发布了一个刚刚作出的艰难决定:“按破坏麦苗数加倍赔偿”。 作为曹操的得力军师,你希望为主公分忧,妥善解决这个问题:

埋伏所在麦田分成了n*m方格,每格麦苗数不同。其中有k个格子将作为埋伏点,需要设立道路,使得每2个埋伏点之间均相通。主公希望剩下的麦苗越多越好。

Input Format
第一行 n m k(1<=n,m<=100 nm<=200 1<=k<=7)。 接下来为nm的矩阵,表示第i行j列的格子的麦苗数。 最后k行,每行包含2个整数,表示(i,j)格为埋伏点。(0<=i < n 0< = j < m)
Output Format
输出麦苗最多能剩余多少

Sample Input
4 4 4
2 7 9 5
4 3 5 5
7 6 7 5
8 2 1 7
1 0
0 1
1 1
0 2
Sample Output
60


  • 分析:
    k <= 7,类似棋盘,考虑状压DP。

  • 用f[i][x][y]表示当前设置埋伏状态为i(0:未设/ 1:设),到达格子为(x,y)时需要破坏的最少麦苗。
    状压DP,转移方程:

f[i][x][y] = min(f[i][x][y], f[i - j][x][y] + f[j][x][y] - a[x][y]);
  • 注意到每次循环完状态i应做一遍类似spfa的bfs,把到达其他格子时状态相同的情况计算出来。

  • 代码:
#include <bits/stdc++.h>
 using namespace std;

 const int g[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
 int n, m, k, x, y, i, j, head, tail, s, ans, a[105][105], q[10005][2], f[1 << 8][105][105];
 bool v[105][105];

 inline int read()
 {
    int x = 0, w = 1;
    char ch = 0;
    while(ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
     }
    while(ch >= '0' && ch <= '9')
          x = x * 10 + ch - '0', ch = getchar();
    return x * w;
  } //读入优化

 inline void write(int x)
 {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
 }//输出优化

 inline void spfa(int p)
 {
    int i, x1, y1, x2, y2;

    for(head = 1; head <= tail; head ++)
    {
        x1 = q[head][0], y1 = q[head][1];
        v[x1][y1] = false;
        for(i = 0; i < 4; i ++)
        {
            x2 = x1 + g[i][0], y2 = y1 + g[i][1];
            if (x2 >= 0 && x2 < n && y2 >= 0 && y2 < m)
                if(f[p][x2][y2] > f[p][x1][y1] + a[x2][y2])
                {
                    f[p][x2][y2] = f[p][x1][y1] + a[x2][y2];
                    if (! v[x2][y2])
                        v[x2][y2] = true, q[++ tail][0] = x2, q[tail][1] = y2;
                }
        }
    }
 }

 int main()
 {
    n = read(), m = read(), k = read();
    for(i = 0; i < n; i ++)
        for(j = 0; j < m; j ++)
        {
            a[i][j] = read();
            ans += a[i][j];
        }
    memset(f, 0x3f, sizeof(f));
    for(i = 0; i < k; i ++)
    {
        x = read(), y = read();
        f[1 << i][x][y] = a[x][y];
    }
    //读入&初始化 
    for(i = 1; i < (1 << k); i ++)
    {
        tail = 0;
        for(x = 0; x < n; x ++)
            for(y = 0; y < m; y ++)
            {
                for(j = 1; j <= i; j ++)
                if ((i & j) == j)
                    f[i][x][y] = min(f[i][x][y], f[i - j][x][y] + f[j][x][y] - a[x][y]);
                if (f[i][x][y] != 0x3f3f3f3f)
                    q[++ tail][0] = x, q[tail][1] = y, v[x][y] = true; 
            }
        spfa(i);
    }
    //状压DP
    s = 0x3f3f3f3f;
    for(x = 0; x < n; x ++)
        for(y = 0; y < m; y ++)
            s = min(s, f[(1 << k) - 1][x][y]);
    ans -= s;
    write(ans);
    //比较并输出答案 
    return 0;

 }

新的一天~【女生每月必备的腰酸。。OTL。。太菜了吧。忽视它忽视它忽视它。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值