小、小、小K的农场!

本题选自洛谷训练场。。。传送门,点击!

First.

思路分析

  1. 此题虽作为一道“提高加省选减”的题,但是对于博主我还是贼简单
  2. 在粗略的审题后,可以轻易地知道,这,是一道图论题!!!
  3. 作为一个真正的OIer,一定要有一个直觉,见到了不等式加图应该想到一种神奇的算法——差分约束系统!
  4. 差分约束系统有两个关键点,一是建图的方向与权值;二是需建超级源点,与每点距离为零;
  5. 原理请搜索“三角不等式”;

Second.

此处介绍两种方法:

No.1:最短路

#include <iostream>
#include <iomanip>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll maxn = 50010;
ll k,n,m;
ll in[maxn];
ll dis[maxn];
ll head[maxn];
bool vis[maxn];
struct edge
{
    ll v,w,nxt;
    edge(){}
    edge(ll _v,ll _w,ll _nxt)
    {
        v=_v;
        w=_w;
        nxt=_nxt;
    }
}e[maxn];
void init()
{
    memset(head,-1,sizeof(head));
    memset(dis,maxn*100,sizeof(dis));
    k=0;
}
void add(ll u,ll v,ll w)
{
    e[k]=edge(v,w,head[u]);
    head[u]=k++;
}
bool spfa(ll u)
{
    queue<ll> q;
    q.push(u);
    vis[u]=true;
    dis[u]=0;
    in[u]=1;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        vis[u]=false;
        for(ll i=head[u];~i;i=e[i].nxt)
        {
            ll v=e[i].v;
            ll w=e[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=true;
                    in[v]++;
                    if(in[v]>20)
                        return false;
                }
            }
        }
    }
    return true;
}
int main()
{
//	freopen("farm.in","r",stdin);
//	freopen("farm.out","w",stdout);
    ios::sync_with_stdio(false);
    init();
    ll mod,a,b,c;
    cin>>n>>m;
    for(ll i=1;i<=n;i++)
        add(0,i,0);
    while(m--)
    {
        cin>>mod;
        switch(mod)
        {
            case 1:{
                cin>>a>>b>>c;//a-b>=c->a>=b+c->0>=b-a+c->-c>=b-a
                add(a,b,-c);
            };break;
            case 2:{
                cin>>a>>b>>c;//a-b<=c
                add(b,a,c);
            };break;
            case 3:{
                cin>>a>>b;//a==b
                add(a,b,0);
                add(b,a,0);
            };break;
        }
    }
    if(spfa(0))
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
//	fclose(stdin);fclose(stdout);
    return 0;
}
/*
3 3
3 1 2
1 1 3 1
2 2 3 2
*/

NO.2:最长路

#include <iostream>
#include <iomanip>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll maxn = 50010;
ll k,n,m;
ll in[maxn];
ll dis[maxn];
ll head[maxn];
bool vis[maxn];
struct edge
{
    ll v,w,nxt;
    edge(){}
    edge(ll _v,ll _w,ll _nxt)
    {
        v=_v;
        w=_w;
        nxt=_nxt;
    }
}e[maxn];
void init()
{
    memset(head,-1,sizeof(head));
    memset(dis,-maxn*100,sizeof(dis));
    k=0;
}
void add(ll u,ll v,ll w)
{
    e[k]=edge(v,w,head[u]);
    head[u]=k++;
}
bool spfa(ll u)
{
    queue<ll> q;
    q.push(u);
    vis[u]=true;
    dis[u]=0;
    in[u]=1;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        vis[u]=false;
        for(ll i=head[u];~i;i=e[i].nxt)
        {
            ll v=e[i].v;
            ll w=e[i].w;
            if(dis[v]<dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=true;
                    in[v]++;
                    if(in[v]>20)
                        return false;
                }
            }
        }
    }
    return true;
}
int main()
{
//	freopen("farm.in","r",stdin);
//	freopen("farm.out","w",stdout);
    ios::sync_with_stdio(false);
    init();
    ll mod,a,b,c;
    cin>>n>>m;
    for(ll i=1;i<=n;i++)
        add(0,i,0);
    while(m--)
    {
        cin>>mod;
        switch(mod)
        {
            case 1:{
                cin>>a>>b>>c;//a-b>=c->a>=b+c->0>=b-a+c->-c>=b-a
                add(a,b,-c);
            };break;
            case 2:{
                cin>>a>>b>>c;//a-b<=c
                add(b,a,c);
            };break;
            case 3:{
                cin>>a>>b;//a==b
                add(a,b,0);
                add(b,a,0);
            };break;
        }
    }
    if(spfa(0))
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
//	fclose(stdin);fclose(stdout);
    return 0;
}
/*
3 3
3 1 2
1 1 3 1
2 2 3 2
*/

Third.
此题为什么会有最短路与最长路的算法但是最后却都能得到正确的答案?

这是因为差分约束系统中主体程序为SPFA,是为一种可以判断正环和负环的最短(长)路算法,当我们反向亦或是正向建图建图时,如果图中存在环,则对于最短路存在负环,而对于最长路存在正环。

所以,不论是哪种算法都可以得到正确的解;

P.S.我在SPFA中将入队次数改为了20次而非n+1次是因为有可能会超时,但还有一种更保险的方式是计算出每一个点的度数,来进行判断,不在此处赘述;


Fourth.

共勉吧,诸君!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值