题干:
链接:https://ac.nowcoder.com/acm/contest/369/F
来源:牛客网
题目描述
现在你有 n 把灵剑,其中选择第i把灵剑会得到的 wiw_iwi 攻击力。
于此同时,还有q个约束,每个约束形如:
x 和 y 表示两个物品的编号,如果同时选中可以获得额外 v0v_0v0 的攻击力, 同时不选可以获得额外 v1v_1v1 点攻击力,只选择一个则会扣除 v2v_2v2 的攻击力。
王小D想知道剑阵的最大攻击力。
九州大陆的未来,必有灵剑山的一笔!
输入描述:
第一行两个整数 n, q ,n表示灵剑数量,q表示约束数量。
接下来一行,共 n 个整数,第 i 个整数表示 wiw_iwi。
接下来 q 行,每行五个整数,表示一个约束。
输出描述:
共一行,输出最大的攻击力。
示例1
输入
复制
5 2
4 2 6 6 2
4 2 4 2 2
5 1 6 6 4
输出
复制
30
说明
5把灵剑都选 ,获得4+2+6+6+2+4+6=30的攻击力
备注:
,且 均为偶数。
数据保证对于任一无序对(x,y)只会有一个约束。
解题报告:
还是看官方题解吧、、、但是注意构造的权值不能出现负数,也不知道为什么。(可能是因为不然不能放到网络流里面跑吧?)
另外注意这题不能直接上来就加st到i的边和i到ed的边,因为你这样的话w[i]会被计算多次,不符合定义式,因为你需要他只被选择一次的。
AC代码:
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
int n,Q;
int tot;
struct Edge {int to,ne;ll w;
} e[100005 * 2];
int head[10005];
int st,ed;
int dis[10050],q[10005];//一共多少个点跑bfs,dis数组和q数组就开多大。
void add(int u,int v,ll w) {e[++tot].to=v; e[tot].w=w; e[tot].ne=head[u]; head[u]=tot;e[++tot].to=u; e[tot].w=0; e[tot].ne=head[v]; head[v]=tot;
}
bool bfs(int st,int ed) {memset(dis,-1,sizeof(dis));int front=0,tail=0;q[tail++]=st;dis[st]=0;while(front<tail) {int cur = q[front];if(cur == ed) return 1;front++;for(int i = head[cur]; i!=-1; i = e[i].ne) {if(e[i].w&&dis[e[i].to]<0) {q[tail++]=e[i].to;dis[e[i].to]=dis[cur]+1;}}}if(dis[ed]==-1) return 0;return 1;
}
ll dfs(int cur,ll limit) {//limit为源点到这个点的路径上的最小边权 if(limit==0||cur==ed) return limit;ll w,flow=0;for(int i = head[cur]; i!=-1; i = e[i].ne) { if(e[i].w&&dis[e[i].to]==dis[cur]+1) {w=dfs(e[i].to,min(limit,e[i].w));e[i].w-=w;e[i^1].w+=w;flow+=w;limit-=w;if(limit==0) break;}}if(!flow) dis[cur]=-1;return flow;
}
ll dinic() {ll ans = 0;while(bfs(st,ed)) ans+=dfs(st,0x7fffffffff);return ans;
}
ll w[100005];
ll W[100005],W2[100005];
int main()
{int Q;cin>>n>>Q;st=n+1,ed=st+1; tot=1;for(int i = 0; i<=ed; i++) head[i] = -1;ll sum = 0,v0,v1,v2,a,b,c,d,e,f;int x,y;for(int i = 1; i<=n; i++) scanf("%lld",w+i),sum += w[i];while(Q--) {scanf("%d%d%lld%lld%lld",&x,&y,&v0,&v1,&v2);sum += v0+v1;a=b=v1/2;c=d=(v0+v1)/2+v2;f=v0/2;e=v0/2;W[x]+=a;W[y]+=b;W2[x]+=e;W2[y]+=f;//add(st,x,a); add(st,y,b); add(x,y,c); add(y,x,d); //add(x,ed,e); add(y,ed,f);}for(int i = 1; i<=n; i++) add(st,i,W[i]),add(i,ed,w[i]+W2[i]);printf("%lld\n",sum - dinic()); return 0;
}
/*
4 2
1 1 1 1
1 2 4 8 2
2 3 6 8 10
*/
不知道为什么还可以这样建图(贴一发别人的代码)(有空再研究吧)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e7 + 10;
int head[N], rest[N], to[N], flow[N], tot = 1;
void sig(int u, int v, int w) {to[++ tot] = v, flow[tot] = w, rest[tot] = head[u], head[u] = tot;
}
void add(int u, int v, int w) {sig(u, v, w), sig(v, u, 0);
}
int n, m, cnt, dis[N], S, T; ll ans;
int bfs() {for(int i = 1 ; i <= cnt ; ++ i) dis[i] = -1;queue<int> q;q.push(S), dis[S] = 1;while(q.size()) {int u = q.front(); q.pop();for(int i = head[u] ; i ; i = rest[i]) {int v = to[i];if(dis[v] == -1 && flow[i]) {dis[v] = dis[u] + 1;q.push(v);}}}return dis[T] != -1;
}
int dfs(int u, int f) {if(u == T || !f) return f;int use = 0;for(int i = head[u] ; i ; i = rest[i]) {int v = to[i];if(flow[i] && dis[v] == dis[u] + 1) {int a = dfs(v, min(f - use, flow[i]));flow[i] -= a, flow[i ^ 1] += a;use += a;if(use == f) break;}}if(!use) dis[u] = -1;return use;
}
int main() {scanf("%d%d", &n, &m);S = ++ cnt, T = ++ cnt;for(int i = 1, a, b ; i <= n ; ++ i) {scanf("%d", &b);a=0;int x = ++ cnt;add(S, x, a);add(x, T, b);ans += a + b;}for(int i = 1, u, v, a, b, c ; i <= m ; ++ i) {scanf("%d%d%d%d%d", &u, &v, &b, &a, &c);u += 2, v += 2;int x = ++ cnt, y = ++ cnt;add(S, x, a); add(x, u, a); add(x, v, a);add(u, y, b); add(v, y, b); add(y, T, b);add(u, v, c); add(v, u, c);ans += a + b;}while(bfs()) {ans -= dfs(S, 0x3f3f3f3f);}printf("%lld\n", ans);
}