【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的公式打得还慢

后半学期,也请各位继续关注:
《我的青春线代物语果然有问题》
《高数女主养成计划》
《程设の旅》
《青春猪头少年不会梦到多智能体吃豆人》
《某Linux的开源软件》
还有——
《我的算法竞赛不可能这么可爱》
本期到此结束!

浙公网安备 33010602011771号