题库链接
D. Miku and Generals
解题思路:按照题意可知给出的冲突关系肯定能够构成二分图,我们可以直接染色,存在冲突的关系可以看作一个连通块,每个联通块我们看作存在三种事件①选择染色为1 ②选择染色为0 ③什么都不选择 然后我们可以把问题转换为背包问题,约束条件:每个联通块里面选择一个事件。
代码:
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 205;
int n, m, value[maxn], sum;
int c0, c1, color[maxn], num[maxn][3];
int dp[maxn << 9];
struct node {
int e, p;
} load[maxn * 2];
int head[maxn], sign;
void add_edge(int s, int e) {
load[++sign] = node{e, head[s]};
head[s] = sign;
}
void dfs(int s, int c) {
color[s] = c;
if(c == 1)
c1 += value[s];
else
c0 += value[s];
for(int i = head[s]; ~i; i = load[i].p) {
int e = load[i].e;
if(color[e] == -1)
dfs(e, c ^ 1);
}
}
void init() {
sum = sign = 0;
memset(head, -1, sizeof(head));
memset(color, -1, sizeof(color));
memset(dp, 0, sizeof(dp));
}
int main() {
int t, s, e;
while(~scanf("%d", &t)) {
while(t--) {
init(); ///初始化
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &value[i]);
value[i] /= 100; ///注意题意
sum += value[i];
}
while(m--) {
scanf("%d %d", &s, &e);
add_edge(s, e);
add_edge(e, s);
}
int up = 0; ///连通块的数量
for(int i = 1; i <= n; i++) {
if(color[i] == -1) {
c0 = c1 = 0;
dfs(i, 1);
num[++up][0] = c1;
num[up][1] = c0;
num[up][2] = 0;
}
}
for(int i = 1; i <= up; i++) {
for(int j = sum / 2; j >= 0; j--) {
for(int k = 0; k <= 2; k++) {
if(j >= num[i][k])
dp[j] = max(dp[j], dp[j - num[i][k]] + num[i][k]);
}
}
}
printf("%d\n", max(dp[sum / 2], sum - dp[sum / 2]) * 100);
}
}
return 0;
}
M. Travel
解题思路:题目要求最小花费,其实就是求最小等级,能够想到二分答案,然后跑最短路,就是多了一个边的距离约束条件(记得long long ,自己强行 int 爆精度,WA在最后一组)
代码:
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
typedef long long ll;
struct node {
int e;
ll c;
int p;
bool friend operator<(node a, node b) {
return a.c > b.c;
}
} load[maxn << 1];
int head[maxn], sign;
void add_edge(int s, int e, int c) {
load[++sign] = node{e, c, head[s]};
head[s] = sign;
}
int n, m, s, c, d, e;
ll dis[maxn];
int vis[maxn];
bool judge(int ans) {
for(int i = 1; i <= n; i++) {
dis[i] = INF;
vis[i] = 0;
}
dis[1] = 0;
priority_queue<node>q;
q.push(node{1, 0});
while(!q.empty()) {
node w = q.top();
q.pop();
int u = w.e, v;
if(vis[u])
continue;
vis[u] = 1;
for(int i = head[u]; ~i; i = load[i].p) {
v = load[i].e;
if(!vis[v] && dis[v] > dis[u] + 1 && load[i].c <= 1LL * ans * d) {
dis[v] = dis[u] + 1;
q.push(node{v, dis[v]});
}
}
}
return dis[n] <= 1LL * ans * e;
}
void init() {
memset(head, -1, sizeof(head));
}
int main() {
init();
int u, v, cost;
scanf("%d %d %d %d %d", &n, &m, &c, &d, &e);
while(m--) {
scanf("%d %d %d", &u, &v, &cost);
add_edge(u, v, cost);
add_edge(v, u, cost);
}
int l = 0, r = maxn, ans = -1, mid;
while(l <= r) {
mid = (l + r) >> 1;
if(judge(mid)) {
ans = mid;
r = mid - 1;
} else
l = mid + 1;
}
if(ans == -1)
printf("-1\n");
else
printf("%lld\n", 1LL * ans * c);
return 0;
}