Description
有 2n 个人玩拔河,拔河的绳子由左右两段组成,每段绳子上有 n 个位置,第 i 个人可以在左边绳子的 li 位置处,也可以在右边绳子的 ri 位置处。每个位置上有且仅有一个人。每个人有一个实力值 si ,问对于每一种合法方案两边实力值和之差的绝对值最小是多少,如果无解输出 -1 。
Input
第1行一个整数 n。
第2 ~ 2n+1行,每行三个整数li, ri, si
第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
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 }
来源:oschina
链接:https://my.oschina.net/u/4361539/blog/3857383