HDU,4370, 0 or 1

HDU 4370:

这一题网上题解很多,不过都很精简。我就写一个详细的吧
原题连接:
http://acm.hdu.edu.cn/showproblem.php?pid=4370
题目中的三个条件:

i=2nX1 , i=1i=1n1Xi , n=1k=1nXi, k=k=1nXk, i    i[2,n1]

第三个条件非常有意思,限制了矩阵 X 的行和列 .

第三个条件给的很隐晦。它隐含了一些信息。

(当然有经验的人就不觉得隐晦了。 因人而异吧 。有人会觉得这是一个很明显的暗示)
假设:开始的时候 。X中所有元素为 0
如果我们给 : X[r][c]=1其中 rc
那么也有:

k=1nX[c][k]=k=1nX[k][c]=1
此时。我们必须选择一个t.令 X[c][t]=1 ct
为了维护性质我们似乎要更改一连串的位置。

这是我最初想到的。这样的选择会重复下去。
我们记录更改的下标。那么会有:

[r][c]>[c][t]

如果我们继续更改位置。或许会有:

[r][c]>[c][t]>[t][p]

什么时候能停能:
例如:

[r][c]>[c][t]>[t][p]>[p][r]

当然上面可以停,也可以不停止。因为对于行和列的限制并不一定是1.

看上去。上面像一条路径。

我们似乎可以把 X 看作邻接矩阵
如果条件 3 转化为每个点的出度等于入度。
很明显。惊奇的发现。X就是邻接矩阵

(说真的。想到这里。想了很久。开始是想这个题目或许是构造的)

因为 条件3 对 点1和n 不起作用。 在不经过1与n的路径中。必然会走一条欧拉回路,或者没有路(行列 的 限制为 0 的时候)

欧拉回路是可以一笔画成的回路。 你可以理解成。他是一个圆环。被扭转在一起。路线之间会有交点。 这个交点就是重复走过的点。但是每个路线边只允许走一次。路径上不允许有重复走过的长度。(数学上。点是没有长度和面积的)

我们把 C[i][j] 看作 i> j这条边的长度
那么最短的回路: 必然是独立的环 或者 一条简单路 (扭在一起多走的环。必然增加答案)

通过上面的分析。限制:

=<=1

就可以得到 答案。
这个限制也包含 点1 与 点 n 这是因为。总出度=总入度。
而点1点出度被限制。 点 n的入度被限制。

到这里。基本快要解决问题了。但我们还没有交代清楚。
题目中还有两个条件:

i=2nX1 , i=1i=1n1Xi , n=1

这个条件比较有趣。他是不受条件3限制。
这也就是说 。

11n1

那么我们就要分类讨论。
很明显。第 1 个节点 的 入度要 和 第 n 个节点的出度保持一致。
这是因为:
总出度 等于 总入度。 又因为两个点的出度和入度被限制。
点1的入度=点n的出度。
其他点的出度=入度
所以 点1的 入度 与 点n 的出度保持一致。
这样才可以使的:总出度=总入度

那么答案有两种情况:
1:1>n21n
对于第二种情况:
我们记 从1出发。环经过的最后一个点事 A 枚举 A
环的长度就是 dis1[A]+C[A][1] ;
从n出发。环的最后一个点事 B。枚举B
环的长度就是 dis2[B]+C[B][n]
其中
dis1[A]A1dis2[B]Bn
tmp=mini[2,n]j[1,n1](dis1[i]+C[i][1]+dis2[j][n]+C[j][n])
answer=min(tmp,dis1[n])
将C作为图来计算两种距离 dis1,   dis2

下面是代码:

#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>
#define MAXN 305

using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;

int n;
int C[MAXN][MAXN];
int dis1[MAXN];
int dis2[MAXN];
priority_queue<P,vector<P>,greater<P>>Q;
void init();

void spfa(int s,int *dis,int edge[][MAXN])
{
    dis[s]=0;
    Q.push(P(0,s));
    while(!Q.empty())
    {
        P x=Q.top();    Q.pop();
        if(x.first>dis[x.second])continue;
        int v=x.second;
        int d=x.first;
        for(int i=0;i<n;i++)
        {
            int w=d+edge[v][i];
            if(w>=dis[i])continue;
            dis[i]=w;
            Q.push(P(dis[i],i));
        }
    }
}

int main ()
{
    while(scanf("%d",&n)==1&&n)
    {
        init();
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",C[i]+j);

        spfa(0  , dis1, C);
        spfa(n-1, dis2, C);

        int ans=dis1[n-1];
        for(int i=1;i<n;i++)
            for(int j=0;j<n-1;j++)
                ans=min(ans,dis1[i]+C[i][0]+dis2[j]+C[j][n-1]);
        printf("%d\n",ans);
    }
    return 0;
}
void init()
{
    memset(dis1,0x3f,sizeof dis1);
    memset(dis2,0x3f,sizeof dis2);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值