JZOJ4406. 【HNOI2016模拟3.27】拔河

87 篇文章 0 订阅

Description

有 2n 个人玩拔河,拔河的绳子由左右两段组成,每段绳子上有 n 个位置,第 i 个人可以在左边绳子的 li 位置处,也可以在右边绳子的 ri 位置处。每个位置上有且仅有一个人。每个人有一个实力值 si ,问对于每一种合法方案两边实力值和之差的绝对值最小是多少,如果无解输出 -1 。

Input

第1行一个整数 n。
第2 ~ 2n+1行,每行三个整数li, ri, si

Output

一个整数表示所求的答案

Sample Input

6
1 4 12
6 1 3
2 4 5
3 1 13
2 6 15
4 2 8
5 6 9
5 2 14
3 3 5
6 5 10
4 5 15
1 3 13

Sample Output

6

Data Constraint

30%:1 <= n <= 10
70%:1 <= n <= 103
100%:1 <= n <= 3 * 104, 1 <= si <= 15

题解

考虑连边,显然 liri l i 与 r i 连一条边,
如果某个点的度数为1,显然这只有一种方案,
如果某个点的度数为0,显然是没有一种合法的方案。
如果就将这些没有的点删掉,
那么剩下的就一定是环。
对于一个环的情况只有两种,奇数边和偶数边。
对于每一个环,贡献是单独的。
用背包解决。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 60003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int n,x,y,z,ans;
int nxt[N*2],lst[N],to[N*2],tot,v[N*2],fo[N*2];
int sum,s[N*2],p,T,t[N*15],ans_,S;
bool vis[N],f[N*15];

void dfs(int x,int fa)
{
    vis[x]=1;
    for(int i=lst[x];i;i=nxt[i])
        if(fa!=(i>>1))
        {
            if(vis[to[i]] && (i>>1)!=(T>>1))p++,T=i;else
            if(!vis[to[i]])sum+=v[i],s[to[i]]=s[x]+v[i],dfs(to[i],i>>1);
        }
}

void ins(int x,int y,int z)
{
    nxt[++tot]=lst[x];
    to[tot]=y;
    v[tot]=z;
    fo[tot]=x;
    lst[x]=tot;
}

int main()
{
    read(n);tot=1;
    for(int i=1;i<=n*2;i++)
        read(x),read(y),read(z),ins(x,y+n,-z),ins(n+y,x,z);

    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n*2;i++)
        if(!vis[i])
        {
            sum=p=T=0;
            dfs(i,0);
            if(p!=1)
            {
                P('-'),P('1');
                return 0;
            }

            x=sum-(s[fo[T]]<<1)-v[T];
            y=sum-(s[to[T]]<<1)+v[T];
            if(x>y)swap(x,y);z=y-x;

            S=S+z;ans_=ans_+x;t[z]++;
        }

    f[0]=1; 
    for(int i=1;i<=S;i++)
        if(t[i])
        {
            for(int j=S;j>=0;j--)
                if(f[j])
                {
                    for(int k=1;k<=t[i] && !f[j+i*k];k++)f[j+i*k]=1;
                }
        }

    ans=2147483647;
    for(int i=0;i<=S;i++)
        if(f[i])ans=min(ans,abs(ans_+i));

    printf("%d",ans);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值