澳门娱乐6165题目链接,然后每次枚举更大的答案

作者:澳门娱乐

题目链接

题目:

对于边带权的有向图,找出一个点数最小的环,使得环上的边权和为负. 2 <= n <= 300.

50分做法

挺显然的一个做法,因为金币量是单调的(如果你花i枚金币可以得到最优解,i+1枚也一定可以),所以可以二分答案

然后对于二分出来的每个答案,都做一遍dp,效率$O(n^2logn)$

澳门娱乐6165 1澳门娱乐6165 2

#include <cstdio>
#include <cstring>
using namespace std;
#define N 500100
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return x*f;
}
int n,d,k,a[N],s[N],dp[N];
int max(int x,int y){return x>y?x:y;}
bool check(int g){
    memset(dp,128,sizeof(dp));
    int t1=(d-g)?d-g:1,t2=d+g,mx=0;
    dp[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<i;j++){
            if(a[i]-a[j]>=t1&&a[i]-a[j]<=t2)
                dp[i]=max(dp[i],dp[j]+s[i]);
        }
    }
    for(int i=1;i<=n;i++)mx=max(dp[i],mx);
    if(mx>=k)return 1;
    return 0;
}
int main(){
    n=read();d=read();k=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),s[i]=read();
    int ans=0,l=0,r=10000000;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid-1,ans=mid;
        else l=mid+1;
    }
    if(ans)printf("%dn",ans);
    else puts("-1");
    return 0;
}

50分做法

题解:

我们可以考虑从小到大枚举答案. 然后每次枚举更大的答案的时候就从当前的较小的答案更新过去. 更具体一点,可以设f[澳门娱乐6165,i][j]表示当前的步数下从i走到j的最短路. 每次更新本质就是一个简单的动态规划的状态转移. 但是这样复杂度是$O(n4)$的. 肯定跑不过去. 更近一步地,从刚才的思路转变一下. 我们设$f[d][i][j]$表示$i \to j$走$2d$步时的最短路. 我们可以在$O(n3\log n)$的复杂度内预处理出来整个数组. ~~震惊! $n3log n$竟可以跑过300 !!!~~ 然后我们利用这个数组将上面的从小到大枚举答案变为类似二分的过程! 也就是说我们可以枚举答案的二进制位. 不难发现如果$(010)_2$成立那么$(100)_2$也成立,所以答案具有单调性. 所以二分即可.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;static char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 510;
const int inf = 0x3f3f3f3f;
int dis[9][maxn][maxn];
int f[maxn][maxn],g[maxn][maxn];
int main(){
    int n,m;read(n);read(m);
    int u,v,w;
    rep(i,1,n) rep(j,1,n) if(i != j) dis[0][i][j] = inf;
    rep(i,1,m){
        read(u);read(v);read(w);
        dis[0][u][v] = w;
    }
    rep(d,1,8){
        rep(i,1,n) rep(j,1,n){
            if(i != j) dis[d][i][j] = inf;
            else dis[d][i][j] = 0;
        }
        rep(k,1,n) rep(i,1,n) rep(j,1,n){
            dis[d][i][j] = min(dis[d-1][i][k]+dis[d-1][k][j],dis[d][i][j]);
        }
    }
    rep(i,1,n) rep(j,1,n){
        if(i != j) g[i][j] = inf;
        else g[i][j] = 0;
    }
    bool flag;
    int ans = 0;
    per(d,8,0){
        rep(i,1,n) rep(j,1,n) f[i][j] = g[i][j];
        rep(k,1,n) rep(i,1,n) rep(j,1,n){
            f[i][j] = min(f[i][j],min(g[i][k]+dis[d][k][j],dis[d][i][k]+g[k][j]));
        }
        flag = false;
        rep(i,1,n) if(f[i][i] < 0){
            flag = true;
            break;
        }
        if(flag) continue;
        rep(i,1,n) rep(j,1,n) g[i][j] = f[i][j];
        ans |= 1 << d;
    }
    if(++ans > n) puts("0");
    else printf("%dn",ans);    return 0;
}

100分做法

考虑怎么让效率降下来

50分的思路没问题,尝试一下能不能让每次dp的效率降下来

观察到答案其实也是单调的,dp[i]的答案是从前面i-d-g个数转移过来的,所以可以使用单调队列优化

总复杂度就变成$O(nlogn)$,能过100分的数据了

澳门娱乐6165 3澳门娱乐6165 4

#include <cstdio>
#include <cstring>
#define ll long long
inline ll read(){
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x*f;
}
using namespace std;
#define inf (1<<30)
ll n,d,k,a[500010],s[500010];
ll dp[500010];
ll q[10000000];
ll max(ll x,ll y){return x>y?x:y;}
bool check(ll g){
    ll l=1,r=0,p=0,t1=max(d-g,1),t2=d+g;
    q[1]=0;
    for(int i=1;i<=n;i++){
        dp[i]=-inf;
        while(a[i]-a[p]>=t1&&p<i){
            while(l<=r&&dp[p]>=dp[q[r]])r--;
            q[++r]=p++;
        }
        while(a[i]-a[q[l]]>t2&&l<=r)l++;
        if(l>r||dp[q[l]]==-inf)continue;
        dp[i]=dp[q[l]]+s[i];
        if(dp[i]>=k)return 1;
    }
    return 0;
}
int main(){
    n=read(),d=read(),k=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),s[i]=read();
    a[0]=0,s[0]=0;
    ll l=0,r=1000000000,ans=-1;
    while(l<=r){
        ll mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%lldn",ans);
    return 0;
}

100分做法

 

本文由澳门娱乐6165发布,转载请注明来源

关键词: