AtCoder AGC036D Negative Cycle - solution

1 篇文章 0 订阅

今天做了场学长“lzh”组的ACM模拟赛,而这道毒瘤题作为第一道,并且是最难的。

1.【题目大意】

一张n个点的图,i到 i+1有连边,边权为0。
Snuke会给所有(i,j)连边,如果 i<j,边权为−1,否则为1。
但是Ringo不想要图里有负环,所以他会删去Snuke加的一些边,使得图中没有负环,删除一条边有个代价,问最小的删边代价。

2.【考虑算法】

直接看题,似乎非常懵逼,想不到什么算法,连暴力都有些麻烦。

所以,我们可以从数据入手,仔细看题目,会发现所有边权都是0或者1或者-1,并且不存在负环!所以,我们很容易想到差分约束系统。

3.【细致解法】

令d[i]为从1号点出发到第i号点的最短路。不存在负环说明就有最短路,等价于我们能够给di赋一个数值,使得对于任意一条边e[i,j]我们都有d[j]≤d[i]+e[i,j],三角形不等式。

不存在负环的时候,显然会有d[i]≥d[i+1]。我们不妨考虑d数组差分后的值。

令q[i]=d[i+1]-d[i],考虑一条边i—>j(i<j),它的限制相当于是d[j]≤d[i]-1,即q[i]+q[i+1]+…+q[j-1]≥1。而对于一条边j—>i(i<j),它的限制相当于是d[i]≤d[j]+1,即q[i]+q[i+1]+…+q[j-1]≤1。

然后我们可以证明出,q[i]∈{0,1},这里比较容易,如果q[i]<0原链的差分约束条件就不满足,如果 q[i]>0 则点i+1存在额外的−1入边 (v,i+1),v<i,此时v到i最坏情况可以走一段0链更新,所以q[i]最多只能为1。

也可以更容易的理解为由于边权都是1或者−1并且存在不能删的0边, 在没有负环的情况下d[i]≤d[i+1]+1,所以d[i]-d[i+1]=0/1,从而推出q[i]=d[i]-d[i+1]是0或者1,

然后我们就可以考虑 q[i]是取 0 还是取 1 ,然后删掉不合法的边,这个过程是可以 DP 解决的,对于不满足∑q[i] ≤1的情况,在其跨过第二个 1 的时候统计掉,对于∑q[i] ≥1的情况,对于每一段连续的 0 统计即可,设f[i,j]表示安排好前i位的q值,且强行令qi=1,上一个为1的位置是j,那么考虑枚举k, f[i,j]转移到f[k,i]。最后,删去不满足约束条件的状态,对于(a,b):

1.如果a>b,要删掉所有满足j<b<i<x<a的边。

2.如果a<b,要删掉所有满足j<i<a<b≤x的边。

这些判断在前缀和中搞定。

4.【代码解析】

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
    int x=0; bool f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    if(f) return x;
    return -x;
}
const int N = 500;
LL a[N+3][N+3],s[2][N+3][N+3],f[N+3][N+3];
int n;
void update(LL &x,LL y) {x = x<y?x:y;}
LL sum(int t,int lx,int rx,int ly,int ry){
    return s[t][rx][ry]-s[t][lx-1][ry]-s[t][rx][ly-1]+s[t][lx-1][ly-1];
}//求区间前缀和,用到了容斥的思想,s[t][lx-1][ry]和s[t][rx][ly-1]减去了两次s[t][lx-1][ly-1],而s[t][rx][ry]中抵消了一次,还需增加一次s[t][lx-1][ly-1]。
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1; j<=n; j++){
            if(j==i) continue;//自己和自己没有连边
            scanf("%lld",&a[i][j]);
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            if(i<j) s[0][i][j]=a[i][j];
            s[0][i][j]+=s[0][i][j-1];
        }
        for(int j=1;j<=n;j++)s[0][i][j]+=s[0][i-1][j];
    }
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            if(i>j)s[1][i][j]=a[i][j];
            s[1][i][j]+=s[1][i][j-1];
        }
        for(int j=1;j<=n;j++)s[1][i][j]+=s[1][i-1][j];
    }
    //上面两段是为了dp优化的,有i<j和i>j两种情况,两个二维前缀和维护。
    memset(f,42,sizeof(f)); //下面是三个数相加,防止爆炸,42=127/3。
    f[0][0]=0ll;
    for(int i=0;i<=n; i++)
        for(int j=0;j<max(i,1);j++)
            for(int k=i+1;k<=n;k++){
                LL tmp=f[i][j]+sum(1,k+1,n,j+1,i)+sum(0,i+1,k,i+1,k);
                update(f[k][i],tmp);
                /* 从f[i][j]转移到f[k][j],转移所需的代价是s[1]中(k+1,j+1)至(n,i)的代价加上s[0]中(i+1,i+1)至(k,k)的代价。*/
            }
    LL ans=f[n][1];
    for(int i=1;i<=n;i++)update(ans,f[n][i]);//哪一位q[i]是1都可以,取最小。
    printf("%lld\n",ans);
    return 0;
}

5.【效率分析】

状态数O(n*n),转移O(n),总复杂度O(n^3),可得100分。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Matlab中,AGC(Automatic Gain Control)是一种用于自动调节信号增益的技术AGC系统的作用是保持信号在一个适当的功率范围内,以便有效地处理和分析信号。使用Matlab可以实现数字AGC系统的建模与仿真。 在Matlab中使用Simulink环境进行数字AGC的建模与仿真可以通过以下步骤实现: 1. 使用Simulink库中的信号源模块生成输入信号。可以使用随机信号或特定模式的信号作为输入。 2. 将信号传递到AGC模块,该模块可以从Simulink库中选择或自定义。 3. 配置AGC模块的参数,例如增益平均系数、增加步进、减小步进、参考级别、上限和下限等。 4. 将AGC模块的输出与其他模块连接,例如显示模块或数据处理模块。 5. 运行模型并观察AGC系统的输出结果。 通过以上步骤,您可以使用Matlab中的Simulink环境建立数字AGC模型,并使用Matlab实现数字AGC系统。需要注意的是,在实际应用中,数字AGC系统的复杂程度可能会更高,需要根据具体情况进行调整和优化。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [基于Simulink的数字AGC建模与仿真](https://blog.csdn.net/CodeWG/article/details/130591628)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值