[TopCoder12727]FoxAndCity 题解
题目地址:TopCoder Arena:Practice – TopCoder …
May all the beauty be blessed.
题目地址:TopCoder Arena:Practice – TopCoder Arena、vjudge:CurvyonRails – TopCoder 12432 – Virtual Judge
Cat Carol is the queen of Gridland. Gridland is a rectangular country divided into a grid of unit square cells. Each cell contains either a town or a wasteland. Carol has decided to construct some railroad tracks according to the following rules:
Note that Carol does not require the entire set of tracks to be connected. Configurations that consist of multiple disjoint loops are acceptable, too.
A Curvy is a curious animal indigenous to Gridland. These animals love curves and hate straight things. Some of the towns in Gridland are inhabited by Curvies. If such a town happens to contain a straight segment of tracks (i.e., a segment that connects two opposite sides of the cell), the Curvies in the town are very unhappy.
You are given a String[] field that describes Gridland: each character of each element of field describes one of its cells. The meaning of individual characters follows.
Compute and return the minimal number of towns with unhappy Curvies when the railroad tracks are constructed according to the above constraints. If there is no way to construct the railroads according to the given rules, return -1 instead.
要求在一个方格图上修建火车轨道,使得轨道都能构成自环。注意有一些格子有障碍,还有些格子在放置直轨道的时候产生费用。
类名:CurvyonRails
函数名:getmin
参数:vector <string>
返回值:int
函数声明:int getmin(vector <string> field)
样例#1:
.. ..
样例#1结果:
0
其他样例参见原网站。
参考资料:Topcoder SRM570 900 CurvyonRails – Enigma_aw – 博客园
如果把方格图抽象成普通图,一条轨道会使一个格子产生2的度数。我们考虑对这个方格图黑白染色,以便控制度数/流量平衡的条件,即度数为2。在这里不使用拆点的方法是因为图是无向图,不需要区分出度与入度。
我们可以建立一个源→黑点→白点→汇的网络,其中,所有的边都设置成容量2的边,用于限制度数。但是这样的图显然是不能够符合要求的,因为我们还要区分直轨弯轨与费用问题。对于直轨弯轨,我们发现直轨只产生横向或纵向的2个度数,而弯轨产生的度数是横纵各1的。我们对一个点拆成横向度数和纵向度数两个点,在黑白点的连接时也区分横纵度数连边即可。我们再用一个总点来限流,即现在的网络变为了源→黑点→黑点的横/纵点→白点的横/纵点→白点→汇,其中源→黑点→黑点的横/纵点之间的边都为容量2的边(白点类似)。至于费用问题,由于我们已经区分了横纵点,对于直轨会产生费用的点,我们从总点向横/纵点建边时建一条容量1费用0的边,再建一条容量1费用1的边,就可以表示如果同一方向产生2个度数产生的费用了。
网络上跑最小费用最大流即可,如果不能满流,说明无解。
由于TopCoder要求实现一个class,下面的代码并不是标准输入/输出格式(传统OI题目)的实现方式,但是把调试注释给去掉就可以用标准输入/输出了。
// Code by KSkun, 2018/4
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <string>
#include <queue>
const int MAXN = 100005, INF = 1e9;
struct Edge {
int to, cap, cost, nxt;
} gra[MAXN << 1];
int head[MAXN], tot;
inline void reset() {
memset(head, -1, sizeof(head)); tot = 0;
}
inline void addedge(int u, int v, int cap, int cost) {
gra[tot] = Edge {v, cap, cost, head[u]}; head[u] = tot++;
gra[tot] = Edge {u, 0, -cost, head[v]}; head[v] = tot++;
}
int dis[MAXN], f[MAXN], pre[MAXN], pree[MAXN];
std::queue<int> que;
bool inque[MAXN];
inline bool spfa(int s, int t) {
memset(dis, 0x3f, sizeof(dis));
memset(f, 0, sizeof(f));
que.push(s); inque[s] = true; f[s] = INF; dis[s] = 0;
while(!que.empty()) {
int u = que.front(); que.pop(); inque[u] = false;
for(int i = head[u]; ~i; i = gra[i].nxt) {
int v = gra[i].to;
if(gra[i].cap > 0 && dis[v] > dis[u] + gra[i].cost) {
dis[v] = dis[u] + gra[i].cost;
f[v] = std::min(f[u], gra[i].cap);
pre[v] = u; pree[v] = i;
if(!inque[v]) {
inque[v] = true;
que.push(v);
}
}
}
}
return f[t] > 0;
}
int flow, cost;
inline void mcmf(int s, int t) {
while(spfa(s, t)) {
flow += f[t];
cost += f[t] * dis[t];
for(int i = t; i != s; i = pre[i]) {
gra[pree[i]].cap -= f[t]; gra[pree[i] ^ 1].cap += f[t];
}
}
}
int n, m, S, T;
class CurvyonRails {
public:
inline int getmin(std::vector<std::string> field) {
n = field.size(); m = field[0].size();
S = n * m * 3 + 1; T = S + 1;
reset();
int sum = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
int ii = i - 1, jj = j - 1;
if(field[ii][jj] == 'w') continue;
if(!((i + j) & 1)) {
sum += 2;
addedge(S, (i - 1) * m + j, 2, 0);
if(field[ii][jj] != 'C') {
addedge((i - 1) * m + j, (i - 1) * m + j + n * m, 2, 0);
addedge((i - 1) * m + j, (i - 1) * m + j + n * m * 2, 2, 0);
} else {
addedge((i - 1) * m + j, (i - 1) * m + j + n * m, 1, 0);
addedge((i - 1) * m + j, (i - 1) * m + j + n * m * 2, 1, 0);
addedge((i - 1) * m + j, (i - 1) * m + j + n * m, 1, 1);
addedge((i - 1) * m + j, (i - 1) * m + j + n * m * 2, 1, 1);
}
if(j - 1 >= 1 && field[ii][jj - 1] != 'w') {
addedge((i - 1) * m + j + n * m, (i - 1) * m + j - 1 + n * m, 1, 0);
}
if(j + 1 <= m && field[ii][jj + 1] != 'w') {
addedge((i - 1) * m + j + n * m, (i - 1) * m + j + 1 + n * m, 1, 0);
}
if(i - 1 >= 1 && field[ii - 1][jj] != 'w') {
addedge((i - 1) * m + j + n * m * 2,
(i - 2) * m + j + n * m * 2, 1, 0);
}
if(i + 1 <= n && field[ii + 1][jj] != 'w') {
addedge((i - 1) * m + j + n * m * 2, i * m + j + n * m * 2, 1, 0);
}
} else {
addedge((i - 1) * m + j, T, 2, 0);
if(field[ii][jj] != 'C') {
addedge((i - 1) * m + j + n * m, (i - 1) * m + j, 2, 0);
addedge((i - 1) * m + j + n * m * 2, (i - 1) * m + j, 2, 0);
} else {
addedge((i - 1) * m + j + n * m, (i - 1) * m + j, 1, 0);
addedge((i - 1) * m + j + n * m * 2, (i - 1) * m + j, 1, 0);
addedge((i - 1) * m + j + n * m, (i - 1) * m + j, 1, 1);
addedge((i - 1) * m + j + n * m * 2, (i - 1) * m + j, 1, 1);
}
}
}
}
mcmf(S, T);
if(flow < sum) {
return -1;
} else {
return cost;
}
}
};
/*int main() {
std::string t;
std::vector<std::string> field;
while(!getline(std::cin, t).eof()) {
field.push_back(t);
}
CurvyonRails solver;
printf("%d", solver.getmin(field));
return 0;
}*/
题目地址:洛谷:【P4307】[JSOI2009]球队收益 / 球队预算 – 洛谷、BZOJ:Problem 1449. — [JSOI2009]球队收益
在一个篮球联赛里,有n支球队,球队的支出是和他们的胜负场次有关系的,具体来说,第i支球队的赛季总支出是Cix^2+Diy^2,Di<=Ci。(赢得多,给球员的奖金就多嘛) 其中x,y分别表示这只球队本赛季的胜负场次。现在赛季进行到了一半,每只球队分别取得了a[i]场胜利和b[i]场失利。而接下来还有m场比赛要进行。问联盟球队的最小总支出是多少。
输入格式:
第一行n,m
接下来n行每行4个整数a[i],b[i],Ci,Di
再接下来m行每行两个整数s,t表示第s支队伍和第t支队伍之间将有一场比赛,注意两只队间可能有多场比赛。
输出格式:
输出总支出的最小值。
输入样例#1:
3 3 1 0 2 1 1 1 10 1 0 1 3 3 1 2 2 3 3 1
输出样例#1:
43
对于20%的数据2<=n<=10,0<=m<=20
对于100%的数据2<=n<=5000,0<=m<=1000,0<=di<=ci<=10,0<=a[i],b[i]<=50.
这个的网络模型需要来表示下面m场比赛的胜负情况,我们考虑使用费用流。对比赛和队伍建点,建立源→比赛→参与比赛的两支队伍→汇的网络,其中源→比赛→队伍的边都是容量1费用0的边。
接下来考虑一下队伍→汇的边的费用问题。对于每一支队伍,我们假设他输掉了所有后面的x场比赛,也就是说,假如我们用w代表它赢的比赛,l代表它输的比赛,最开始的时候w=a_i, l=b_i+x,那么它的收益应该是C_iw^2+D_il^2。如果它赢了1场,则w加1,l减1,收益变为了C_i(w+1)^2+D_i(l-1)^2,我们来做一发减法,发现了\Delta = 2C_iw - 2D_il + C + D。我们知道,w越大的时候,l越小,\Delta的值就越大。因此这题就是一个费用递增模型,我们可以对每一个w建一条容量1费用2C_iw - 2D_il + C + D的边,表示每赢一场比赛对答案的贡献。此时这个网络模型就完整了,答案即是默认所有队伍输掉所有比赛的收益+最小费用最大流的费用。
// Code by KSkun, 2018/4
#include <cstdio>
#include <cstring>
#include <queue>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF
: *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 100005, INF = 1e9;
struct Edge {
int to, cap, cost, nxt;
} gra[MAXN << 1];
int head[MAXN], tot;
inline void addedge(int u, int v, int cap, int cost) {
gra[tot] = Edge {v, cap, cost, head[u]}; head[u] = tot++;
gra[tot] = Edge {u, 0, -cost, head[v]}; head[v] = tot++;
}
int f[MAXN], dis[MAXN], pre[MAXN], pree[MAXN];
std::queue<int> que;
bool inque[MAXN];
inline bool spfa(int s, int t) {
memset(f, 0, sizeof(f));
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0; inque[s] = true; f[s] = INF; que.push(s);
while(!que.empty()) {
int u = que.front(); que.pop(); inque[u] = false;
for(int i = head[u]; ~i; i = gra[i].nxt) {
int v = gra[i].to;
if(gra[i].cap > 0 && dis[v] > dis[u] + gra[i].cost) {
pre[v] = u; pree[v] = i;
f[v] = std::min(f[u], gra[i].cap);
dis[v] = dis[u] + gra[i].cost;
if(!inque[v]) {
inque[v] = true;
que.push(v);
}
}
}
}
return f[t];
}
int flow, cost;
inline void mcmf(int s, int t) {
while(spfa(s, t)) {
for(int i = t; i != s; i = pre[i]) {
gra[pree[i]].cap -= f[t];
gra[pree[i] ^ 1].cap += f[t];
}
flow += f[t];
cost += f[t] * dis[t];
}
}
int n, m, a[MAXN], b[MAXN], c[MAXN], d[MAXN], mcnt[MAXN], u, v, sum, S, T;
// 1 ~ n team
// n+1 ~ n+m match
int main() {
memset(head, -1, sizeof(head));
n = readint(); m = readint();
S = n + m + 1; T = S + 1;
for(int i = 1; i <= n; i++) {
a[i] = readint(); b[i] = readint(); c[i] = readint(); d[i] = readint();
}
for(int i = 1; i <= m; i++) {
u = readint(); v = readint();
mcnt[u]++; mcnt[v]++;
addedge(S, i + n, 1, 0);
addedge(i + n, u, 1, 0);
addedge(i + n, v, 1, 0);
}
for(int i = 1; i <= n; i++) {
int w = a[i], l = b[i] + mcnt[i];
sum += c[i] * w * w + d[i] * l * l;
for(int j = 1; j <= mcnt[i]; j++) {
addedge(i, T, 1, 2 * w * c[i] - 2 * l * d[i] + c[i] + d[i]);
w++; l--;
}
}
mcmf(S, T);
printf("%d", sum + cost);
return 0;
}
题目地址:Codeforces:Problem – 311E – Codeforces、洛谷:【CF311E】Biologist – 洛谷
SmallR is a biologist. Her latest research finding is how to change the sex of dogs. In other words, she can change female dogs into male dogs and vice versa.
She is going to demonstrate this technique. Now SmallR has n dogs, the costs of each dog’s change may be different. The dogs are numbered from 1 to n. The cost of change for dog i is vi RMB. By the way, this technique needs a kind of medicine which can be valid for only one day. So the experiment should be taken in one day and each dog can be changed at most once.
This experiment has aroused extensive attention from all sectors of society. There are m rich folks which are suspicious of this experiment. They all want to bet with SmallR forcibly. If SmallR succeeds, the i-th rich folk will pay SmallR wi RMB. But it’s strange that they have a special method to determine whether SmallR succeeds. For i-th rich folk, in advance, he will appoint certain ki dogs and certain one gender. He will think SmallR succeeds if and only if on some day the ki appointed dogs are all of the appointed gender. Otherwise, he will think SmallR fails.
If SmallR can’t satisfy some folk that isn’t her friend, she need not pay him, but if someone she can’t satisfy is her good friend, she must pay g RMB to him as apologies for her fail.
Then, SmallR hope to acquire money as much as possible by this experiment. Please figure out the maximum money SmallR can acquire. By the way, it is possible that she can’t obtain any money, even will lose money. Then, please give out the minimum money she should lose.
有N只狗,性别给定,改变性别花费vi。有M个人,想要让某些狗变成某个性别,如果能满足他们的要求则获得收益wi,否则当这个人是你的朋友的时候则要倒贴g,不是朋友的时候没有倒贴。求最大收益。
输入格式:
The first line contains three integers n, m, g (1 ≤ n ≤ 10^4, 0 ≤ m ≤ 2000, 0 ≤ g ≤ 10^4). The second line contains n integers, each is 0 or 1, the sex of each dog, 0 represent the female and 1 represent the male. The third line contains n integers v1, v2, …, vn (0 ≤ vi ≤ 10^4).
Each of the next m lines describes a rich folk. On the i-th line the first number is the appointed sex of i-th folk (0 or 1), the next two integers are wi and ki (0 ≤ wi ≤ 10^4, 1 ≤ ki ≤ 10), next ki distinct integers are the indexes of appointed dogs (each index is between 1 and n). The last number of this line represents whether i-th folk is SmallR’s good friend (0 — no or 1 — yes).
输出格式:
Print a single integer, the maximum money SmallR can gain. Note that the integer is negative if SmallR will lose money.
输入样例#1:
5 5 9 0 1 1 1 0 1 8 6 2 3 0 7 3 3 2 1 1 1 8 1 5 1 1 0 3 2 1 4 1 0 8 3 4 2 1 0 1 7 2 4 1 1
输出样例#1:
2
输入样例#2:
5 5 8 1 0 1 1 1 6 5 4 2 8 0 6 3 2 3 4 0 0 8 3 3 2 4 0 0 0 3 3 4 1 1 0 10 3 4 3 1 1 0 4 3 3 4 1 1
输出样例#2:
16
我们考虑利用最小割和最大权闭合子图的模型来解决这个问题。
首先对于狗,我们将边的功能定义为割边=变性。因此对于性别0的狗与源连边,性别1的狗与汇连边,容量为变性开销。这是对狗的一次分类。
接下来我们考虑如何处理条件限制。如果用割边来表示不满足一个条件限制,这个是很好做的。关键在于与狗的联系。我们尝试利用类似的方法把条件分类,要求性别为0的条件与源连边,性别1的条件与汇连边,容量为这个条件的收益。对于条件与狗,我们连容量无限的边,表示一种要求,注意有向边的方向,一定要让流可以向不满足条件的狗的方向流,例如性别为0的条件应该向狗连边,而要求性别为1的则要从狗向条件连边。然后我们会发现,与要求条件相同的狗本来就在条件一侧的子图中,这条边永远不会有流流过,而不同的狗则可能有流流过。
我们来思考一条增广路,一定是源→要求0的条件→性别1的狗→汇,割边时要么选择不满足条件,要么选择给狗变性。另外一种情况同理。因此这种建图方式能够满足我们的要求。
此外,还有倒贴的问题。我们可以对条件与源汇连的边加上这个倒贴的g,当用\sum w_i减掉最小割的时候,不能被满足的条件的g也会同时减去。
// Code by KSkun, 2018/4
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF
: *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 100005, INF = 1e9;
struct Edge {
int to, cap, nxt;
} gra[MAXN << 5];
int head[MAXN], tot;
inline void addedge(int u, int v, int cap) {
gra[tot] = Edge {v, cap, head[u]}; head[u] = tot++;
gra[tot] = Edge {u, 0, head[v]}; head[v] = tot++;
}
int level[MAXN];
inline bool bfs(int s, int t) {
memset(level, -1, sizeof(level));
std::queue<int> que;
level[s] = 0; que.push(s);
while(!que.empty()) {
int u = que.front(); que.pop();
for(int i = head[u]; ~i; i = gra[i].nxt) {
int v = gra[i].to;
if(level[v] == -1 && gra[i].cap > 0) {
level[v] = level[u] + 1;
if(v == t) return true;
que.push(v);
}
}
}
return level[t] != -1;
}
int cur[MAXN];
inline int dfs(int u, int t, int left) {
if(u == t || left <= 0) return left;
int flow = 0;
for(int &i = cur[u]; ~i; i = gra[i].nxt) {
int v = gra[i].to;
if(gra[i].cap > 0 && level[v] == level[u] + 1) {
int d = dfs(v, t, std::min(left, gra[i].cap));
if(d > 0) {
left -= d; flow += d;
gra[i].cap -= d; gra[i ^ 1].cap += d;
if(left <= 0) return flow;
}
}
}
return flow;
}
inline int dinic(int s, int t) {
int flow = 0;
while(bfs(s, t)) {
memcpy(cur, head, sizeof(head));
int f;
while(f = dfs(s, t, INF)) {
flow += f;
}
}
return flow;
}
int n, m, g, col[MAXN], vi, coli, wi, ki, dogi, fri, ans, S, T;
// 1 ~ n dog
// n+1 ~ n+m rich folk
int main() {
memset(head, -1, sizeof(head));
n = readint(); m = readint(); g = readint();
S = n + m + 1; T = S + 1;
for(int i = 1; i <= n; i++) {
col[i] = readint();
}
for(int i = 1; i <= n; i++) {
vi = readint();
if(col[i] == 0) addedge(S, i, vi);
else addedge(i, T, vi);
}
for(int i = 1; i <= m; i++) {
coli = readint(); wi = readint(); ki = readint();
ans += wi;
while(ki--) {
dogi = readint();
if(coli == 0) addedge(i + n, dogi, INF);
else addedge(dogi, i + n, INF);
}
fri = readint();
if(coli == 0) addedge(S, i + n, wi + (fri ? g : 0));
else addedge(i + n, T, wi + (fri ? g : 0));
}
ans -= dinic(S, T);
printf("%d", ans);
return 0;
}