【CF 1018(div.2) C】这道DP完全超出了C题的平均水平

这道题是真的难啊

考场上看着就没什么思路,现在补题好几天了还查了一堆资料才补出来
本来DP这玩意就难,这道题打的标签又是1700
1700放C?
再加上我DP确实上学期到现在一题都没做过
手是有点生了 也没办法 学业压力大
不过说实话图论好像看目前CD都没咋考
也许图论学一半 应该转回来练练DP和数据结构了?
说实话这个翻译是有点抽象 我自己翻一遍吧
下面看题目吧:

C:Wonderful City

题目描述

您是古波兰一座城市引以为豪的领导者。这里有 $ n^2 $座建筑,排列成 $ n $ 行和 $ n $ 列。位于第i行第j列的建筑物高度为 $ h_{i,j} $ 。
如果没有两座相邻的建筑高度相同,那么这座城市就是美丽的。换句话说,它必须满足:

  • 不存在一个位置 $ (i,j) $ (\(1 \leq i \leq n\) ,$1 \leq j \leq n-1 $ ) ,使得 $ h_{i,j} = h_{i,j+1} $。
  • 不存在一个位置 $ (i,j) $ ($1 \leq i \leq n-1 $ ,$1 \leq j \leq n $ ) ,使得 $ h_{i,j} = h_{i+1,j} $。

A公司有 $ n $ 名工人,B公司有 $ n $ 名工人。每个工人只能被雇佣最多一次(我要谴责 Codeforces Better,为什么没翻译出来这句?)
雇佣A公司的 $ i $ 工人需要 $ a_i $ 枚硬币,在雇佣之后,工人 $ i $ 将会:

  • 将 $ i $ 行中所有建筑的高度增加 $ 1 $ ,换句话说,将 $ h_{i,1},h_{i,2} \ldots, h_{i,n} $ 增加 $ 1 $ 。

在B公司雇佣工人 $ j $ 需要花费 $ b_j $ 枚硬币。

  • 将 $ j $ 列中所有建筑的高度增加 $ 1 $ 。换句话说,将 $ h_{1,j},h_{2,j} \ldots,h_{n,j} $ 增加 $ 1 $ 。

请计算出使城市变得美丽所需的最少硬币数,或者报告说这是不可能的。

输入

每个测试包含多个测试用例。第一行包含测试用例的数量 \(t\) ( \(1 \le t \le 100\) )。测试用例说明如下。

每个测试用例的第一行都包含一个整数 \(n\) ( \(2 \le n \le 1000\) ) - 网格的大小。

每个测试用例接下来 \(n\) 行中的 \(i\) /-th包含 \(n\) 个整数 \(h_{i, 1}, h_{i, 2}, \ldots, h_{i, n}\)\(1 \le h_{i, j} \le 10^9\) )-- \(i\) 行中建筑物的高度。

每个测试用例的下一行包含 \(n\) 个整数 \(a_1, a_2, \ldots, a_n\) ( \(1 \le a_i \le 10^9\) )--A 公司雇佣工人的成本。

每个测试用例的下一行包含 \(n\) 个整数 \(b_1, b_2, \ldots, b_n\) ( \(1 \le b_j \le 10^9\) ) - 在 B 公司雇佣工人的成本。

保证所有测试用例中 \(n\) 的总和不超过 \(1000\)

输出

对于每个测试用例,输出一个整数--所需的最小硬币数,如果不可能,则输出 $ -1 $ 。

样例输入

4
2
1 2
2 1
100 100
100 100
4
1 2 1 2
3 2 1 2
1 2 1 1
1 3 1 2
1 2 3 4
5 6 7 8
3
1 2 2
2 2 1
2 1 1
100 100 100
100 100 100
6
8 7 2 8 4 8
7 7 9 7 1 1
8 3 1 1 8 5
6 8 3 1 1 4
1 4 5 1 9 6
7 1 1 6 8 2
11 23 20 79 30 15
15 83 73 57 34 63

样例输出

0
14
-1
183

提示

在第一个测试案例中,我们可以看到城市已经很美了。因此,答案为 \(0\)

对于第二个测试案例,我们可以从 A 公司雇佣工人 \(2\) ,从 A 公司雇佣工人 \(4\) ,从 B 公司雇佣工人 \(4\)

\(1\) \(2\) \(1\) \(\color{red}2\) \(\implies\) \(1\) \(2\) \(1\) \(\color{red}3\)
\(\color{red}3\) \(\color{red}2\) \(\color{red}1\) \(\color{red}2\) \(\color{red}4\) \(\color{red}3\) \(\color{red}2\) \(\color{red}4\)
\(1\) \(2\) \(1\) \(\color{red}1\) \(1\) \(2\) \(1\) \(\color{red}2\)
\(\color{red}1\) \(\color{red}3\) \(\color{red}1\) \(\color{red}2\) \(\color{red}2\) \(\color{red}4\) \(\color{red}2\) \(\color{red}4\)

雇用工人的成本是 \(2 + 4 + 8 = 14\) 。这是可能的最低成本。

对于第三个测试案例,无论我们做什么,都不可能让城市变得美丽。因此,答案为 \(-1\)

解法&&个人感想

首先,就是看到每名工人只能雇佣一次
然后,我们需要想到一个事情,就是如果我们雇佣了第 $ i $ 名工人(不妨假设是在第 $ i $ 行上加),此时相对地,相邻列之间的差值是不会变的
所以,我们独立考虑行和列
我们发现,每个行只有两种状态,加1或者不加1,但这不是简单的背包,它有限制条件,就是对于某行,它加完不能跟上一行相等
于是我们建立数组 $ dp[n][2] $ 和 $ dp[i][0] $ 表示第 $ i $ 行没有使用加法的时候的最小值 而 $ dp[i][1] $ 表示第 $ i $ 行使用了加法时的最小值,此时,我们发现 $ dp[i][j] $ 完全由 $ dp[i-1][k] $ 决定 而且每个数组只需枚举4种状态 ($ 2*2 = 4 $ )。
这个时候我们的dp就有思路了,转移方程很简单 就是 $ dp[i][j]=min(dp[i][j],dp[i-1][k]+a[i]) (0 \leq j \leq 1,0 \leq k \leq 1) $
当然,这里转移之前要判断状态是否合法再转移
然后 初值就是 $ dp[1][1]=a[1] $ 和 $ dp[1][0]=0 $ 。
其他值赋成 $ INF $ 注意这里$ INF $ 需要赋成 $ long long $ 类型下的大数 是 $ 1e18 $ 或者 $ 1e17 $。
下面看代码:

#include<bits/stdc++.h>
#define ll long long
#define ull signed long long
using namespace std;
int t;
int n;
const ll INF=1e17;
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        vector<ll>a(n+5,0);
        vector<ll>b(n+5,0);
        vector<vector<ll>>ma(n+5,vector<ll>(n+5,0));
        vector<vector<ll>>fma(n+5,vector<ll>(n+5,0));
        vector<vector<ll>>dp(n+5,vector<ll>(2,INF));
        vector<vector<ll>>fdp(n+5,vector<ll>(2,INF));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                cin>>ma[i][j];
            }
        }
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=1;i<=n;i++){
            cin>>b[i];
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                fma[i][j]=ma[j][i];
            }
        }
        dp[1][0]=0;dp[1][1]=a[1];
        for(int i=2;i<=n;i++){
            for(int j=0;j<=1;j++){
                for(int k=0;k<=1;k++){
                    int flag=1;
                    for(int v=1;v<=n;v++){
                        if(ma[i][v]+j==ma[i-1][v]+k){
                            flag=0;
                            break;
                        }
                    }
                    if(!flag) continue;
                    else dp[i][j]=min(dp[i][j],dp[i-1][k]+(j==0?0:a[i]));
                }
            }
        }        //我们先做加i行的 所以列之间的不变 要把列扫一遍
        ll ans_1=min(dp[n][1],dp[n][0]);
        fdp[1][0]=0,fdp[1][1]=b[1];
        for(int i=2;i<=n;i++){
            for(int j=0;j<=1;j++){
                for(int k=0;k<=1;k++){
                    int flag=1;
                    for(int v=1;v<=n;v++){
                        if(fma[i][v]+j==fma[i-1][v]+k){
                            flag=0;
                            break;
                        }
                    }
                    if(!flag) continue;
                    else fdp[i][j]=min(fdp[i][j],fdp[i-1][k]+(j==0?0:b[i]));
                }
            }
        }
        ll ans_2=min(fdp[n][0],fdp[n][1]);
        if(ans_2==INF||ans_1==INF) printf("-1\n");
        else cout<<ans_1+ans_2<<endl;
    }
    return 0;
}

搞了好几天,真给整破防了
markdown的公式打得还慢
alt text
后半学期,也请各位继续关注:
《我的青春线代物语果然有问题》
《高数女主养成计划》
《程设の旅》
《青春猪头少年不会梦到多智能体吃豆人》
《某Linux的开源软件》
还有——

《我的算法竞赛不可能这么可爱》

本期到此结束!

posted @ 2025-04-24 15:33  elainafan  阅读(32)  评论(0)    收藏  举报