【Codeforces Testing Round #4】Codeforces 147B Smile House

A smile house is created to raise the mood. It has n rooms. Some of
the rooms are connected by doors. For each two rooms (number i and j),
which are connected by a door, Petya knows their value cij — the value
which is being added to his mood when he moves from room i to room j.

Petya wondered whether he can raise his mood infinitely, moving along
some cycle? And if he can, then what minimum number of rooms he will
need to visit during one period of a cycle?

Input The first line contains two positive integers n and m (), where
n is the number of rooms, and m is the number of doors in the Smile
House. Then follows the description of the doors: m lines each
containing four integers i, j, cij и cji
(1 ≤ i, j ≤ n, i ≠ j,  - 104 ≤ cij, cji ≤ 104). It is guaranteed that
no more than one door connects any two rooms. No door connects the
room with itself.

Output Print the minimum number of rooms that one needs to visit
during one traverse of the cycle that can raise mood infinitely. If
such cycle does not exist, print number 0.

在两个限制条件中,一个【权值和为正】是硬性条件,但是我们在求解中需要尽量保证局部最优才能使后面选择余地较大,另一个【边数最少】是要求最优化的答案。两个都要求尽量优是容易顾此失彼的,但是如果我们认为可以在原地停留,也就是把最优值定义为不超过k步的结果,可以推得f[k][i][j]表示至少走k步,从i点到j点,路上的最大权值和,这样的话答案就具有了关于k的单调性,我们可以二分答案之后贪心的选取权值。
但是这样的话复杂度是O(n^4logn),无法承受。因为有许多重复运算,可以用倍增优化,复杂度O(n^3(logn)^2)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int oo=0x3f3f3f3f;
int f[10][310][310],ans[2][310][310],n;
bool ok(int x)
{
    int k,now=0,i,j,p;
    for (i=1;i<=n;i++)
      for (j=1;j<=n;j++)
        ans[0][i][j]=i==j?0:-oo;
    for (k=0;(1<<k)<=x;k++)
      if (x&(1<<k))
      {
        now^=1;
        for (i=1;i<=n;i++)
          for (j=1;j<=n;j++)
            ans[now][i][j]=-oo;
        for (i=1;i<=n;i++)
          for (j=1;j<=n;j++)
            for (p=1;p<=n;p++)
              ans[now][i][j]=max(ans[now][i][j],ans[now^1][i][p]+f[k][p][j]);
      }
    for (i=1;i<=n;i++)
      if (ans[now][i][i]>0) return 1;
    return 0;
}
int main()
{
    int i,j,k,m,p,q,x,y,z,w,l,r,mid;
    scanf("%d%d",&n,&m);
    for (k=0;(1<<k)<=n;k++)
      for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
          f[k][i][j]=i==j?0:-oo;
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&x,&y,&z,&w);
        f[0][x][y]=z;
        f[0][y][x]=w;
    }
    for (k=1;(1<<k)<=n;k++)
      for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
          for (x=1;x<=n;x++)
            f[k][i][j]=max(f[k][i][j],f[k-1][i][x]+f[k-1][x][j]);
    l=2;
    r=n+1;
    while (l<r)
    {
        mid=(l+r)/2;
        if (ok(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d\n",l%(n+1));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值