Yu-Gi-Oh!Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 538 Accepted Submission(s): 152
Problem Description
"Yu-Gi-Oh!", also known as "Dueling Monsters", is a popular trading card game which has nearly 20 years history. Next year, YGO will reach its 20th birthday.
Stilwell has n monsters on the desk, each monster has its leveli and ATKi. There are two kinds of monsters, Tuner monsters and Non-Tuner monsters. Now, Stilwell plans to finish some "Synchro Summon", and "Synchro Summon" is a kind of special summon following these rules (a little different from the standard YGO rules): (1) A "Synchro Summon" needs two monsters as the material of this summon, and they must be one Tuner monster and one Non-Tuner monster. In other words, we can cost one Tuner monster and one Non-Tuner monster to get a Synchro monster ("cost" means remove form the desk, "get" means put on to the desk). (2) To simplify this problem, Synchro monsters are neither Tuner monsters nor Non-Tuner monsters. (3) The level sum of two material must be equal to the level of Synchro monster we summon. For example: A Level 3 Tuner monster + A Level 2 Non-Tuner monster = A Level 5 Synchro Monster A Level 2 Tuner monster + A Level 4 Non-Tuner monster = A Level 6 Synchro Monster A Level 4 Tuner monster + A Level 4 Non-Tuner monster = A Level 8 Synchro Monster (4) The material of some Synchro monster has some limits, the material must contain some specific monster. For example: A Level 5 Synchro Monster α requires A Level 3 Tuner monster α to be its material A Level 6 Synchro Monster β requires A Level 4 Non-Tuner monster β to be its material A Level 8 Synchro Monster γ requires A Level 4 Tuner monster γ + A Level 4 Non-Tuner monster γ to be its material A Level 5 Synchro Monster φ doesn't require any monsters to be its material Then A Level 3 Tuner monster α + A Level 2 Non-Tuner monster = A Level 5 Synchro Monster α A Level 3 Tuner monster δ + A Level 2 Non-Tuner monster ≠ A Level 5 Synchro Monster α A Level 2 Tuner monster + A Level 4 Non-Tuner monster β = A Level 6 Synchro Monster β A Level 3 Tuner monster + A Level 3 Non-Tuner monster ζ ≠ A Level 6 Synchro Monster β A Level 4 Tuner monster γ + A Level 4 Non-Tuner monster γ = A Level 8 Synchro Monster γ A Level 4 Tuner monster σ + A Level 4 Non-Tuner monster γ ≠ A Level 8 Synchro Monster γ A Level 4 Tuner monster γ + A Level 4 Non-Tuner monster ϕ ≠ A Level 8 Synchro Monster γ A Level 3 Tuner monster + A Level 2 Non-Tuner monster = A Level 5 Synchro Monster φ A Level 3 Tuner monster α + A Level 2 Non-Tuner monster = A Level 5 Synchro Monster φ Stilwell has m kinds of Synchro Monster cards, the quantity of each Synchro Monster cards is infinity. Now, given leveli and ATKi of every card on desk and every kind of Synchro Monster cards. Please finish some Synchro Summons (maybe zero) to maximum ∑ATKi of the cards on desk.
Input
The first line of the input contains a single number T,
the number of test cases.
For each test case, the first line contains two integers n, m. Next n lines, each line contains three integers tuneri, leveli, and ATKi, describe a monster on the desk. If this monster is a Tuner monster, then tuneri=1, else tuneri=0 for Non-Tuner monster. Next m lines, each line contains integers levelj, ATKj, rj, and following rj integers are the required material of this Synchro Monster (the integers given are the identifier of the required material). The input data guarantees that the required material list is available, two Tuner monsters or two Non-Tuner monsters won't be required. If ri=2 the level sum of two required material will be equal to the level of Synchro Monster. T≤10, n,m≤300, 1≤leveli≤12, 0≤ATKi≤5000, 0≤ri≤2
Output
T lines,
find the maximum ∑ATKi after
some Synchro Summons.
Sample Input
Sample Output
|
题意:
有N张怪兽卡(编号从1到N),这些怪兽分成两种(因为是按照0和1分类的,这里我们称为0集和1集)且每个怪兽都有各自的等级和战斗力。
给出M个合成卷轴——每张可以由两个怪兽卡合成一个新的怪兽,但是新怪兽的等级必须是两个怪兽等级之和。
对于每个合成出来的怪兽,给出它的等级、战斗力和一个限制条件limit,后面跟着limit个整数代表合成该怪兽必须使用的怪兽卡编号。
已经给出每个新怪兽的等级和战斗力,问你用这N张怪兽卡可以得到的最大的战斗力之和。
分析:赤裸裸的费用流。
按照我AC这道题的历程来说明吧。。。
首先就想到费用流,建图自然而然就出来了。 试了一发,果断TLEo(╯□╰)o
先说下我一开始的建图:
TLE建图:设置超级源点S,超级汇点T
一、S向0集的怪兽建边,容量为1,费用为怪兽的战斗力;
二、1集的怪兽向T建边,容量为1,费用为怪兽的战斗力;
三、对于新的合成怪兽战斗力attack,若大于合成两怪兽战斗力之和sum,则0集怪兽向1集怪兽建边,容量为1,费用为多出的战斗力attck - sum;
四、S向1集怪兽建边,容量为1,费用为0,表示该怪兽不与0集任何怪兽结合;
五、0集怪兽向T建边,容量为1,费用为0,表示该怪兽不与1集任何怪兽结合。
然后跑一次最大费用最大流,提前算了下复杂度,承受不起。。。但我还是提交了一次,果断TLE。
看下我的TLE代码:
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 400
#define MAXM 1000000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], pre[MAXN];
bool vis[MAXN];
int N, M;
void init()
{
edgenum = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
Edge E1 = {u, v, w, 0, c, head[u]};
edge[edgenum] = E1;
head[u] = edgenum++;
Edge E2 = {v, u, 0, 0, -c, head[v]};
edge[edgenum] = E2;
head[v] = edgenum++;
}
int S, T;
struct used//组成物
{
int op, level, attck;
};
used u[MAXN];
struct goal//目标
{
int level, attck, limit;
int can[2];
};
goal g[MAXN];
void getMap()
{
init();
scanf("%d%d", &N, &M);
S = 0, T = N+1;
for(int i = 1; i <= N; i++)
{
scanf("%d%d%d", &u[i].op, &u[i].level, &u[i].attck);
if(u[i].op == 0)
addEdge(S, i, 1, u[i].attck), addEdge(i, T, 1, 0);
else
addEdge(i, T, 1, u[i].attck), addEdge(S, i, 1, 0);
}
for(int i = 1; i <= M; i++)
{
scanf("%d%d%d", &g[i].level, &g[i].attck, &g[i].limit);
//sum += g[i].attck;
for(int j = 0; j < g[i].limit; j++)
scanf("%d", &g[i].can[j]);
}
for(int i = 1; i <= M; i++)
{
if(g[i].limit)//有限制
{
if(g[i].limit == 1)//只有一个限制
{
int a = g[i].can[0];//编号
if(u[a].op == 0)//0集
{
for(int j = 1; j <= N; j++)
{
if(j == a || u[j].level + u[a].level != g[i].level) continue;//等级不匹配
if(u[a].attck + u[j].attck < g[i].attck)
addEdge(a, j, 1, g[i].attck - u[a].attck - u[j].attck);
}
}
else//1集
{
for(int j = 1; j <= N; j++)
{
if(j == a || u[a].level + u[j].level != g[i].level) continue;
if(u[a].attck + u[j].attck < g[i].attck)
addEdge(j, a, 1, g[i].attck - u[a].attck - u[j].attck);
}
}
}
else//两个限制
{
int a = g[i].can[0], b = g[i].can[1];
if(g[i].level == u[a].level + u[b].level && u[a].attck + u[b].attck < g[i].attck)//可以组成
{
if(u[a].op == 0)//0集向1集建边
addEdge(a, b, 1, g[i].attck - u[a].attck - u[b].attck);
else
addEdge(b, a, 1, g[i].attck - u[a].attck - u[b].attck);
}
}
}
else//没有限制 任意组合
{
for(int j = 1; j <= N; j++)
{
for(int k = j+1; k <= N; k++)
{
if(u[k].level + u[j].level == g[i].level && u[j].attck + u[k].attck < g[i].attck)
{
if(u[j].op == 0 && u[k].op == 1)
addEdge(j, k, 1, g[i].attck - u[k].attck - u[j].attck);
if(u[j].op == 1 && u[k].op == 0)
addEdge(k, j, 1, g[i].attck - u[k].attck - u[j].attck);
}
}
}
}
}
}
bool SPFA(int s, int t)
{
queue<int> Q;
memset(dist, -INF, sizeof(dist));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
dist[s] = 0;
vis[s] = true;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dist[E.to] < dist[u] + E.cost && E.cap > E.flow)
{
dist[E.to] = dist[u] + E.cost;
pre[E.to] = i;
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
return pre[t] != -1 && dist[t] >= 0;
}
void MCMF(int s, int t, int &cost)
{
cost = 0;
while(SPFA(s, t))
{
int Min = INF;
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
Edge E = edge[i];
Min = min(Min, E.cap-E.flow);
}
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += Min * edge[i].cost;
}
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
getMap();
int cost;
MCMF(S, T, cost);
printf("%d\n", cost);
}
return 0;
}TLE原因在于建边过多!
没办法,只有转化问题了。
我们做过这样的题目,比如说N个孩子和M个糖果,给你多组关系代表a孩子选b糖果得到的快乐值为c。现在问你能够得到的最大快乐值。怎么建边就不说了,我们会发现,这两个题目很相似。
我们可以用二维数组Map[i][j]来存储0集i怪兽和1集j怪兽可以合成怪兽的最大战斗力,这样按照上述问题来建边,就大大减少了边数。
AC代码建图:设置超级源点S,超级汇点T
一、S向0集怪兽建边,容量为1,费用为怪兽战斗力;
二、1集怪兽向T建边,容量为1,费用为怪兽战斗力;
三、0集怪兽向1集怪兽建边,容量为1,费用为合成怪兽最大战斗力 - 两个怪兽战斗力之和(前提是正数);
四、S向1集怪兽建边,容量为1,费用为0,表示该怪兽不与0集任何怪兽结合;
五、0集怪兽向T建边,容量为1,费用为0,表示该怪兽不与1集任何怪兽结合。
最后跑最大费用最大流就ok了。
AC代码:
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 400
#define MAXM 10000000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], pre[MAXN];
bool vis[MAXN];
int N, M;
void init()
{
edgenum = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
Edge E1 = {u, v, w, 0, c, head[u]};
edge[edgenum] = E1;
head[u] = edgenum++;
Edge E2 = {v, u, 0, 0, -c, head[v]};
edge[edgenum] = E2;
head[v] = edgenum++;
}
int S, T;
struct used//组成物
{
int op, level, attack;
};
used u[MAXN];
struct goal//目标
{
int level, attack, limit;
int can[2];
};
goal g[MAXN];
int Map[MAXN][MAXN];//记录i和j结合可以得到的最大攻击力
int large[MAXN];
void getMap()
{
init();
scanf("%d%d", &N, &M);
S = 0, T = N+1;
for(int i = 1; i <= N; i++)
{
scanf("%d%d%d", &u[i].op, &u[i].level, &u[i].attack);
if(u[i].op == 0)
addEdge(S, i, 1, u[i].attack), addEdge(i, T, 1, 0);
else
addEdge(i, T, 1, u[i].attack), addEdge(S, i, 1, 0);
}
memset(Map, 0, sizeof(Map));
for(int i = 1; i <= M; i++)
{
scanf("%d%d%d", &g[i].level, &g[i].attack, &g[i].limit);
for(int j = 0; j < g[i].limit; j++)
scanf("%d", &g[i].can[j]);
int rec = 0;//记录可以匹配的最大攻击力
if(g[i].limit)//有限制
{
if(g[i].limit == 1)//只有一个限制
{
int a = g[i].can[0];//编号
for(int j = 1; j <= N; j++)
{
if(j == a) continue;
if(u[a].op != u[j].op && u[a].level + u[j].level == g[i].level)
{
if(u[a].op == 0)
Map[a][j] = max(Map[a][j], g[i].attack);
else
Map[j][a] = max(Map[j][a], g[i].attack);
}
}
}
else//两个限制
{
int a = g[i].can[0], b = g[i].can[1];
if(u[a].op != u[b].op && u[a].level + u[b].level == g[i].level)//可以组成
{
if(u[a].op == 0)
Map[a][b] = max(Map[a][b], g[i].attack);
else
Map[b][a] = max(Map[b][a], g[i].attack);
}
}
}
else//没有限制 任意组合
{
for(int j = 1; j <= N; j++)
{
for(int k = 1; k <= N; k++)
{
if(k == j) continue;
if(u[k].op != u[j].op && u[k].level + u[j].level == g[i].level)
{
if(u[j].op == 0)
Map[j][k] = max(Map[j][k], g[i].attack);
else
Map[k][j] = max(Map[k][j], g[i].attack);
}
}
}
}
}
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= N; j++)
{
if(Map[i][j] > u[i].attack + u[j].attack)
addEdge(i, j, 1, Map[i][j] - u[i].attack - u[j].attack);
}
}
}
bool SPFA(int s, int t)
{
queue<int> Q;
memset(dist, -INF, sizeof(dist));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
dist[s] = 0;
vis[s] = true;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dist[E.to] < dist[u] + E.cost && E.cap > E.flow)
{
dist[E.to] = dist[u] + E.cost;
pre[E.to] = i;
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
return pre[t] != -1 && dist[t] >= 0;
}
void MCMF(int s, int t, int &cost)
{
cost = 0;
while(SPFA(s, t))
{
int Min = INF;
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
Edge E = edge[i];
Min = min(Min, E.cap-E.flow);
}
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += Min * edge[i].cost;
}
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
getMap();
int cost;
MCMF(S, T, cost);
printf("%d\n", cost);
}
return 0;
}
本文介绍了一个关于费用流问题的实际案例,通过调整建图策略减少边的数量,从而显著提高算法效率。文章详细解释了如何利用二维数组存储可能的合成效果,并据此优化建边过程,最终实现快速求解。
1万+

被折叠的 条评论
为什么被折叠?



