小、小、小K的农场!

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

First.

思路分析

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

Second.

此处介绍两种方法:

No.1:最短路

#include 
#include 
#include 
#include 
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 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"<

NO.2:最长路

#include 
#include 
#include 
#include 
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 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]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"<

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

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

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

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


Fourth.

共勉吧,诸君!

你可能感兴趣的:(差分约束系统,图论,SPFA)