题解:P12976 受力分析 Force

这边是题目传送门喵!

题意简述

形式化题面说的很清楚了。这里说一个通俗一点的:一个表格初始全部为 \(0\),给定其中每一个点 \((i,j)\) 对应值的取值范围是一个闭区间 \([L_{i,j},R_{i,j}]\)。需要你一行一行上加一个数,再一列一列加一个数,使最后表中所有数都能满足给定的区间,同时最小化给定序列的字典序。

这是样例的图,下面一张对应了将数据填入后的表格长什么样。

思路

前排提醒:为了便于理解,提前明确行与列,\(x_i\) 对应的是第 \(i\) 横行加的数,\(y_i\) 对应的是第 \(i\) 横行加的数。

从每一个点进行分析。对于点 \((i,j)\),有 \(L_{i,j} \leq x_i +y_j \leq R_{i,j}\) 成立。

其实这就很像差分约束了。最开始的时候我产生了两种思路,这里都罗列出来吧:

  1. 对于两个点 \(P(i_P,j_P),Q(i_Q,j_Q)\),如果不在同一行或列则不予以考虑,下面分为两类:

    • \(P,Q\) 在同一行,即 \(i_P=i_Q=i\) 时,则有:

      \[\begin{cases} L_{i,j_P}\leq x_i+y_{j_P}\leq R_{i,j_P} ,\\ L_{i,j_Q}\leq x_i+y_{j_Q}\leq R_{i,j_Q} \end{cases} \]

      两式相减,得到:

      \[L_{i,j_P}-R_{i,j_Q} \leq y_{j_P}-y_{j_Q}\leq R_{i,j_P} - L_{i,j_Q} \]

      这个很明显是一个差分约束的基本形式了。通过这种约束的方法,每一行能有 \((n-1)^2\) 个约束条件,一共有 \(n\) 行。

    • 同样的道理,也可以给纵列施加同样的约束条件。

      \(P,Q\) 在同一列,即 \(j_P=j_Q=i\) 时,则有:

      \[\begin{cases} L_{i_P,j}\leq x_{i_P}+y_j\leq R_{i_P,j} ,\\ L_{i_Q,j}\leq x_{i_Q}+y_j\leq R_{i_Q,j} \end{cases} \]

      同样也是两式相减,得到:

      \[L_{i_P,j}-R_{i_Q,j} \leq x_{i_P}-x_{i_Q}\leq R_{i_P,j} - L_{i_Q,j} \]

    • 这么算下来,约束的数量级就是 \(n^3\)。貌似不是很可做。于是想了新的方法。

  2. 直接将 \(y\) 取反。将每一个原始条件改写成 \(L_{i,j} \leq x_i -(-y_j) \leq R_{i,j}\) 然后差分约束就可以了。

    这个思路还是比较简单的。问题是如果 \(x_i < 0\)\(-y_j > 0\) 怎么办?

    显而易见,\(x_i -(-y_j) = (x_i + d) - (-y_j + d)\),因此我们将对于每一个点,使之满足条件的最大的 \(d\) 求出来就可以使所有 \(x_i,y_j\) 在合法范围内。最后再看是不是每个 \(-y_j\) 仍然满足约束就可以了。

代码

#include<bits/stdc++.h>
#define ll long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N=505;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int n,L[N][N],R[N][N],d[N][N],x[N],y[N];
struct side{
    int to,w;
};
vector<side>g[N<<1];
void add(int u,int v,int w){
    g[u].push_back({v,w});
    return;
}

int cnt[N<<1],dis[N<<1];
bool inq[N<<1];

bool spfa(int n){
    for(int i=0;i<=n;i++)dis[i]=-inf,inq[i]=0,cnt[i]=0;
    dis[0]=0;
    queue<int>q;
    q.push(0),inq[0]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop(),inq[u]=0;
        for(auto s:g[u]){
            int v=s.to;
            if(dis[v]<dis[u]+s.w){
                dis[v]=dis[u]+s.w;
                if(inq[v])continue;
                if(++cnt[v]>n+1)return 1;
                q.push(v),inq[v]=1;
            }
        }
    }
    return 0;
}


int main(){
    ios;cin>>n;
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>L[i][j];
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>R[i][j];
    for(int i=1;i<=n;i++)add(0,i,0),add(n+i,0,0);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            // L[i][j]<=x[i]-(-y[j])<=R[i][j]
            add(n+j,i,L[i][j]);
            add(i,n+j,-R[i][j]);
        }
    }
    if(spfa(2*n)){
        cout<<"-1\n";
        return 0;
    }
    int minn=inf;
    for(int i=1;i<=n;i++)minn=min(minn,dis[i]); // 最小增加的
    for(int i=1;i<=n;i++)x[i]=dis[i]-minn;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            d[i][j]=L[i][j]-(dis[i]-minn); // 合法范围内最小化 y[j] 的值
        }
    }
    for(int j=1;j<=n;j++){
        y[j]=0;
        for(int i=1;i<=n;i++)y[j]=max(y[j],d[i][j]);
        // 这个 max 的意思是满足所有点合法
    }
    bool flag=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(L[i][j]>x[i]+y[j]||x[i]+y[j]>R[i][j]){
                flag=1;break;
            }
        }
    }
    if(flag){
        cout<<"-1\n";
        return 0;
    }
    for(int i=1;i<=n;i++)cout<<x[i]<<' ';
    cout<<'\n';
    for(int i=1;i<=n;i++)cout<<y[i]<<' ';
    cout<<'\n';
    return 0;
}

尾声: 如果有人能用我的第一种方法做出来欢迎来跟我讨论喵!

感谢阅读!

posted @ 2026-01-29 13:01  Circle_Table  阅读(8)  评论(0)    收藏  举报