[dfs][dp] Jzoj P4406 拔河

故事扮演 提交于 2021-02-11 04:11:30

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

 

 

题解

  • 我们可以将l[i]和r[i]+n连一条边,然后我们发现这就是一个二分图
  • 然后因为每个点都要有一个人,如果一个点度数为0,那么显然要输出-1
  • 如果度数只有1的话,这个点是绝对确定的
  • 那么对于剩下的点,度数都为2,会形成若干偶环
  • 对于每一个偶环,只会有两个种取值方式,顺着走或逆着走,就会产生两个权值,a和b
  • 然后,可以把|a-b|当成一个物品来做背包问题,直接背包,时间复杂度就是O(n*∑si),显然过不了
  • 考虑如何优化,可以把背包改成多重背包

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5 struct edge { int x,y,from,v; }e[60010*2];
 6 int tot,n,boo,to,mx,sum1,sum2,ans,cnt=1,f[60010*15],visit[60010],head[60010],sum[60010],k[60010*15];
 7 void insert(int x,int y,int v) { e[++cnt].y=y; e[cnt].x=x; e[cnt].from=head[x]; head[x]=cnt; e[cnt].v=v; }
 8 void dfs(int x,int k)
 9 {
10     visit[x]=1;
11     for (int i=head[x];i;i=e[i].from)
12         if ((i^1)!=k)
13             if (!visit[e[i].y]) sum[e[i].y]=sum[x]+e[i].v,mx+=e[i].v,dfs(e[i].y,i);
14             else 
15                 if (i/2!=to/2)
16                 {
17                     if (boo) { printf("-1"),exit(0); }
18                     boo=1,to=i;
19                 }
20 }
21 int main()
22 {
23     scanf("%d",&n);
24     for (int i=1;i<=2*n;i++)
25     {
26         int l,r,s;
27         scanf("%d%d%d",&l,&r,&s);
28         insert(l,r+n,-s),insert(r+n,l,s);
29     }
30     for (int i=1;i<=2*n;i++)
31         if (!visit[i])
32         {
33             boo=mx=to=0;
34             dfs(i,0);
35             int a=mx-2*sum[e[to].x]-e[to].v,b=mx-2*sum[e[to].y]+e[to].v;
36             if (a>b) swap(a,b);
37             k[b-=a]++,sum1+=a,sum2+=b;
38             
39         }
40     f[0]=1,ans=abs(sum1);
41     for (int i=1;i<=sum2;i++)
42         if (k[i])
43             for (int j=sum2;j>=0;j--)
44                 if (f[j])
45                     for (int z=1;z<=k[i]&&!f[i*z+j];z++) f[i*z+j]=1;
46     for (int i=1;i<=sum2;i++) if (f[i]) ans=min(ans,abs(sum1+i));
47     printf("%d",ans);
48     return 0;
49 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!