链接:https://www.nowcoder.com/acm/contest/206/A
来源:牛客网
恬恬的生日临近了。宇扬给她准备了一个蛋糕。
正如往常一样,宇扬在蛋糕上插了n支蜡烛,并把蛋糕分为m个区域。因为某种原因,他必须把第i根蜡烛插在第ai个区域或第bi个区域。区域之间是不相交的。宇扬在一个区域内同时摆放x支蜡烛就要花费x2的时间。宇扬布置蛋糕所用的总时间是他在每个区域花的时间的和。
宇扬想快些见到恬恬,你能告诉他布置蛋糕最少需要多少时间吗?
第一行包含两个整数n,m(1 ≤ n ≤ 50, 2≤ m≤ 50)。 接下来n行,每行两个整数ai,bi(1 ≤ ai, bi ≤ m)。
一个整数表示答案。
示例1
复制
3 3 1 2 1 2 1 2
复制
5
示例2
复制
3 3 1 2 2 3 1 3
复制
3
解题思路:最直接的思路,暴力搜索,枚举2^50种情况。加上剪枝可过(记录最小的答案)。另外一种思路,要使的和最小,肯定是要使数字尽可能地平均,因此用优先队列维护每一个数字,然后不断地尝试把最大的那个数字移到其他数字里去,看看答案会不会减小,然后更新答案。
最好的思路是费用流。
每个蜡烛建一个点。每个区域建一个点
建立一个超级源点连向每一个蜡烛,流量为1,费用为0.
建立一个超级汇点,每一个区域向汇点连边,记为边集E,流量为INF,费用初始为1.
每个蜡烛向他的区域连边,流量为1,费用为0.
我们比较关心边集E,这里的边流量每+1,他的费用都要更新,因此我们每次SPFA后,都要判断增广路径是否经过这个边集E,然后更新这条边的费用,反向边也要更新。
如果不想更新费用,可以对于每一个区域再新建50个节点,每个节点的费用为平方数之差,这样子直接跑就OK。
新建50个的代码
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 4010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
struct edge
{
int u, v, cap, cost, next;
} e[4 * MAXN];
int edge_num;
int head[MAXN];
void insert_edge(int u, int v, int cap, int cost)
{
e[edge_num].u = u;
e[edge_num].v = v;
e[edge_num].cap = cap;
e[edge_num].cost = cost;
e[edge_num].next = head[u];
head[u] = edge_num++;
e[edge_num].u = v;
e[edge_num].v = u;
e[edge_num].cap = 0;
//注意这里
e[edge_num].cost = -cost;
//注意这里
e[edge_num].next = head[v];
head[v] = edge_num++;
}
int dis[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool spfa(int s, int t)
{
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
dis[s] = 0;
vis[s] = 1;
queue que;
que.push(s);
while (!que.empty())
{
int tp = que.front();
que.pop();
vis[tp] = 0;
for (int i = head[tp]; ~i; i = e[i].next)
{
int v = e[i].v;
int cost = e[i].cost;
if (e[i].cap && dis[v] > dis[tp] + cost)
{
dis[v] = dis[tp] + cost;
pre[v] = i;
if (!vis[v])
{
vis[v] = 1;
que.push(v);
}
}
}
}
if (dis[t] == INF)
return false;
return true;
}
pair MCMF(int s, int t)
{
int maxflow = 0;
int mincost = 0;
int minc;
while (spfa(s, t))
{
minc = INF;
int cost = 0;
for (int i = pre[t]; ~i; i = pre[e[i].u])
minc = min(minc, e[i].cap);
for (int i = pre[t]; ~i; i = pre[e[i].u])
{
e[i].cap -= minc;
e[i ^ 1].cap += minc;
cost += minc * e[i].cost; //flow*unit cost=total cost
}
mincost += cost;
maxflow += minc;
}
return make_pair(mincost, maxflow);
}
int tot = 0;
int in[MAXN];
int out[MAXN];
int ssout[MAXN][51];
int S, SK, T;
int main()
{
edge_num = 0;
memset(head, -1, sizeof(head));
tot = 0;
int N, M;
scanf("%d%d", &N, &M);
S = ++tot;
for (int i = 1; i <= N; i++)
in[i] = ++tot;
for (int i = 1; i <= M; i++)
{
out[i] = ++tot;
for(int j=1;j<=50;j++){
ssout[i][j]=++tot;
}
}
T = ++tot;
int a,b;
for(int i=1;i<=N;i++){
scanf("%d%d",&a,&b);
insert_edge(in[i],out[b],1,0);
insert_edge(in[i],out[a],1,0);
}
for(int i=1;i<=M;i++){
for(int j=1;j<=50;j++){
insert_edge(out[i],ssout[i][j],1,j*j-(j-1)*(j-1));
}
}
for(int i=1;i<=N;i++){
insert_edge(S,in[i],1,0);
}
for(int i=1;i<=M;i++){
for(int j=1;j<=50;j++)
insert_edge(ssout[i][j],T,1,0);
}
printf("%d\n", MCMF(S, T).first);
return 0;
}
修改边费用的代码
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 4010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
struct edge
{
int u, v, cap, cost, next;
} e[4 * MAXN];
int edge_num;
int head[MAXN];
int insert_edge(int u, int v, int cap, int cost)
{
e[edge_num].u = u;
e[edge_num].v = v;
e[edge_num].cap = cap;
e[edge_num].cost = cost;
e[edge_num].next = head[u];
head[u] = edge_num++;
e[edge_num].u = v;
e[edge_num].v = u;
e[edge_num].cap = 0;
//注意这里
e[edge_num].cost = -cost;
//注意这里
e[edge_num].next = head[v];
head[v] = edge_num++;
return edge_num-2;
}
int wm[5006];
bool im[MAXN];
bool om[MAXN];
int dis[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool spfa(int s, int t)
{
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
dis[s] = 0;
vis[s] = 1;
queue que;
que.push(s);
while (!que.empty())
{
int tp = que.front();
que.pop();
vis[tp] = 0;
for (int i = head[tp]; ~i; i = e[i].next)
{
int v = e[i].v;
int cost = e[i].cost;
if (e[i].cap && dis[v] > dis[tp] + cost)
{
dis[v] = dis[tp] + cost;
pre[v] = i;
if (!vis[v])
{
vis[v] = 1;
que.push(v);
}
}
}
}
if (dis[t] == INF)
return false;
return true;
}
pair MCMF(int s, int t)
{
int maxflow = 0;
int mincost = 0;
int minc;
while (spfa(s, t))
{
minc = INF;
int cost = 0;
for (int i = pre[t]; ~i; i = pre[e[i].u])
minc = min(minc, e[i].cap);
for (int i = pre[t]; ~i; i = pre[e[i].u])
{
e[i].cap -= minc;
e[i ^ 1].cap += minc;
cost += minc * e[i].cost; //flow*unit cost=total cost
if(im[i]){
mincost-=(wm[e[i].cost]-1)*(wm[e[i].cost]-1);
e[i].cost=(wm[e[i].cost]+1)*(wm[e[i].cost]+1);
}
if(om[i]){
mincost+=(wm[-e[i].cost]-1)*(wm[-e[i].cost]-1);
e[i].cost=-(wm[-e[i].cost]+1)*(wm[-e[i].cost]+1);
}
}
mincost += cost;
maxflow += minc;
}
return make_pair(mincost, maxflow);
}
int tot = 0;
int in[MAXN];
int out[MAXN];
int S, SK, T;
int main()
{
for(int j=1;j<5000;j++)
for(int i=1;i<=50;i++)
if(i*i==j)
wm[j]=i;
edge_num = 0;
memset(head, -1, sizeof(head));
tot = 0;
int N, M;
scanf("%d%d", &N, &M);
S = ++tot;
for (int i = 1; i <= N; i++)
in[i] = ++tot;
for (int i = 1; i <= M; i++)
{
out[i] = ++tot;
}
T = ++tot;
int a,b;
for(int i=1;i<=N;i++){
scanf("%d%d",&a,&b);
insert_edge(in[i],out[b],1,0);
insert_edge(in[i],out[a],1,0);
}
for(int i=1;i<=M;i++){
int ee=insert_edge(out[i],T,N,1);
im[ee]=1;
om[ee+1]=1;
}
for(int i=1;i<=N;i++){
insert_edge(S,in[i],1,0);
}
printf("%d\n", MCMF(S, T).first);
return 0;
}