链接:https://ac.nowcoder.com/acm/contest/17933/I
来源:牛客网
题目描
龙龙同学最近迷上了MC,一天,他进入了一个服务器,腐竹非常友好的送了他一个神器,这个神器可以选择一行或一列的区域,将该区域每个顶层方块上增加一个方块或者消去每个顶层方块,他想在一些矩形区域内建一些漂亮的火柴盒,但这些区域内凹凸不平,他是个强迫症,他希望通过该神器对这些区域进行修理,并且使这些区域y轴为0(即海拔为0),由于他希望成为一个时间管理大师,他想知道修理每个区域的最短时间时多少,这可是个伤脑筋的问题,于是他来请求你的帮助,你能写个程序帮他快速解决这个问题吗?
某区域如下图:
令下界合金块层y轴为0;
初始图:
修剪后:
该区域可化简为如下矩阵
2 1 1 2
0 -1 -1 0
1 0 0 1
0 -1 -1 0
你可以对该矩阵的每行每列进行+1或-1操作,每次操作需花费1单位时间,你需求出将该矩阵所有元素都变成0 的最少时间。
输入描述:
第一行一个整数n(1<=n<=1000)
接下来一个n*n的矩阵,矩阵中每个元素aij有-1e9<=aij<=1e9。
输出描述:
输出一个整数,代表最少时间。
解题思路:
本题是个差分约束的题。对该知识不了解的-->https://www.cnblogs.com/zzz-hhh/p/11200893.html
对于一个矩阵,我们将行列分别编号为1~n,设对i行的操作次数为xi,对j列的操作次数为yj,这样对于矩阵中每个元素aij,我们有xi+yj=aij,可化为xi>=(-yj)+aij&&(-yj)>=xi-aij,这样转换后我们得到了一个不等式组,对于这样一系列不等式求最值我们可以用差分约束来写,对于矩阵中每个元素aij,我们可以建立两条边,即xi到(-yj)有条权值为-aij的边,(-yj)到xi有条权值为aij的边(-yi只是一个编号,我们将它编号为j+n),这样我们就得到了一个有向图,对这个图我们再建立一个超级源点,超级源点到图中每个点的有条权值为0的边,现在这个问题就转变成了求超级源点到图中其他点的最长路问题,但求出最长路并不是最终答案,因为我们要求的是最少操作次数,最少操作次数即为每个点最长路减去所有点的中位数绝对值之和。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6+10;
const int M = 2e6+10;
ll n,m;
ll e[M],w[M],ne[M],h[N],idx;
ll dis[N];
bool vis[N];
void add(ll a,ll b,ll c){
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void spfa(){
memset(dis,-0x3f3f3f3f,sizeof(dis));
memset(vis,false,sizeof(vis));
queue<int>Q;
for(int i=1;i<=2*n;i++){
Q.push(i);
dis[i]=0;
vis[i]=true;
}
while(!Q.empty()){
int t=Q.front();
Q.pop();
vis[t]=false;
for(int i=h[t];~i;i=ne[i]){
int y=e[i];
if(dis[y]<dis[t]+w[i]){
dis[y]=dis[t]+w[i];
if(!vis[y]){
Q.push(y);
vis[y]=true;
}
}
}
}
}
int main(){
memset(h,-1,sizeof(h));
idx=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ll a;
scanf("%lld",&a);
add(j+n,i,a);
add(i,j+n,-a);
}
}
spfa();
sort(dis+1,dis+1+2*n);
ll ans=0;
for(int i=1;i<=2*n;i++){
ans+=abs(dis[i]-dis[n]);
}
cout<<ans<<endl;
}
由于矩阵一定有解,我们也可一用迪杰斯特拉来写,虽然有负边,当我们求最长路时,加一个1e9即可,即dis[j] < dis[v] + w[i] + maxn。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define Inf 0x3f3f3f3f
const int maxn = 1e9+10;
const int N = 2e5 + 10;
const int M = 2e7 + 10;
int n, m;
inline int read()
{
int x = 0, y = 1;char c = getchar();
while (c < '0' || c>'9') { if (c == '-') y = -1;c = getchar(); }
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * y;
}
int e[M], w[M], ne[M], h[N], idx;
ll dis[N], cnt[N];
bool vis[N];
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
void dijk() {
memset(dis, -Inf, sizeof(dis));
memset(vis, false, sizeof(vis));
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>Q;
for (int i = 0;i < 2 * n;i++) {
Q.push({ 0,i });
dis[i] = 0;
}
while (Q.size()) {
auto t = Q.top();
Q.pop();
int v = t.second;
if (vis[v]) {
continue;
}
vis[v] = true;
for (int i = h[v];~i;i = ne[i]) {
int j = e[i];
if (dis[j] < dis[v] + w[i] + maxn) {
dis[j] = dis[v] + w[i];
Q.push({ dis[j],j });
}
}
}
}
int main() {
n=read();
memset(h, -1, sizeof(h));
idx = 0;
int ma = -1;
for (int i = 0;i < n;i++) {
for (int j = 0;j < n;j++) {
int a;
a=read();
ma = max(ma, abs(a));
add(j + n, i, a);
add(i, j + n, -a);
}
}
dijk();
sort(dis, dis + 2 * n);
ll ans = 0;
for (int i = 0;i < 2 * n;i++) {
ans += abs(dis[i] - dis[n - 1]);
}
cout << ans << endl;
}