P 2016 战略游戏
先喂一波题意:给一颗树,选最小点覆盖(选定一个点,与它相连的那些点也将被覆盖)
#include
#include
#include
#include
using namespace std;
const int maxn=1505;
int n,cur;
//树形dp
int dp[1505][4];
//链式前向星存边
int to[maxn*2],nxt[maxn*2],head[maxn];
void add(int x,int y){
to[++cur]=y;
nxt[cur]=head[x];
head[x]=cur;
}
void dfs(int u,int fa){
dp[u][0]=0;dp[u][1]=1;
for(int i=head[u];i;i=nxt[i]){
if(to[i]==fa) continue;
dfs(to[i],u);
dp[u][0]+=dp[to[i]][1];
dp[u][1]+=min(dp[to[i]][0],dp[to[i]][1]);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int id,k;
cin>>id>>k;
for(int j=1;j<=k;j++){
int a;
cin>>a;
add(id,a);
add(a,id);
}
}
dfs(0,-1);
cout<<min(dp[0][0],dp[0][1])<<endl;
return 0;
}
秀无敌啊
策略 如下:从叶子开始处理,对每一个正在处理的节点,查看它是否被点亮,如果未被点亮,点亮它的父亲(点父亲总不会比点亮自己差,父亲可能连接更多的点,如果已经被点亮,就暂时不需点亮它的父亲(不一定需要),再看它父亲的其他节点;当发现它父亲的儿子已经全被点亮而它父亲还未被点亮,将父亲放入队列后续判断。
最后统计点亮个数即可。
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1505;
int n,cur,root,c[maxn],fa[maxn],ans;
bool li[maxn];
vector<int> v[maxn];
queue<int> q;
int main(){
cin>>n;
for(int i=0;i<n;i++) fa[i]=-1;
for(int i=1;i<=n;i++){
int id,k;
cin>>id>>k;
c[id]+=k;
for(int j=1;j<=k;j++){
int x;
cin>>x;
fa[x]=id;
v[id].push_back(x);
}
}
for(int i=0;i<n;i++){
if(v[i].size()==0){
q.push(i);
}
if(fa[i]==-1) root=i;
}
//n=1要特判一下 因为n=1进不了循环
if(n==1) {
cout << 1<<endl;
return 0;
}
while(q.front()!= root){
int now=q.front();
q.pop();
c[fa[now]]--;
if(!li[now]) li[fa[now]]=1;
if(!c[fa[now]]) q.push(fa[now]);
}
for(int i=0;i<n;i++)
if(li[i]) ans ++ ;
cout << ans<<endl;
return 0;
}
小结论:
最小覆盖数==最大匹配数(如果是无向图 == 最大匹配数/2)
#include
#include
#include
#include
#include
using namespace std;
const int maxn=7000;
int n,ans,cnt,head[maxn],to[maxn*2],match[maxn*2],nxt[maxn*2];
bool vis[maxn];
void add(int x,int y){
to[++cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}
bool dfs(int x){
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(!vis[y]){
vis[y]=1;
if(match[y]==-1|| dfs(match[y])){
match[y]=x;
return 1;
}
}
}
return 0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int id,k;
cin>>id>>k;
for(int j=1;j<=k;j++){
int x;
cin>>x;
add(x,id);
add(id,x);
}
}
for(int i=0;i<n;i++) match[i]=-1;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++) vis[j]=0;
if(dfs(i)) ans ++;
}
cout << ans/2 <<endl;
return 0;
}
P1525
两种做法-
并查集带权,实现基于贪心的思想。把冲突值递减排列,尽量把值大的两个人划分到不同的监狱。有一个enemy数组,用来存放一个人的敌人团队。
如果冲突,就直接输出,因为已经按照最优策略排列。
// ConsoleApplication5.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5 + 5;
int N, M, con[maxn],enemy[maxn],f[maxn];
struct R {
int u, v, w;
bool operator <(const R r) {
return w > r.w;
}
}r[maxn];
void ini() {
for (int i = 1;i <= N;i++) {
f[i] = i;
}
}
//这里都是板子
int find(int x) {
return x == f[x] ? x : f[x]=find(f[x]);
}
void merge(int x, int y) {
f[find(x)] = f[find(y)];
return;
}
int main() {
cin >> N >> M;
for (int i = 1;i <= M;i++) {
cin >> r[i].u >> r[i].v >> r[i].w;
}
ini();
sort(r + 1, r + 1 + M);
for (int i = 1;i <= M;i++) {
//cout << r[i].u << " " << r[i].v << " " << r[i].w << endl;
int p1 = find(r[i].u);
int p2 = find(r[i].v);
//如果这两个人的阵营被安排了,直接输出
if (p1 == p2) {
cout << r[i].w << endl;
return 0;
}
//设置不同阵营
if (!enemy[r[i].u]) {
enemy[r[i].u] = r[i].v;
}
else {
merge(enemy[r[i].u], r[i].v);
}
if (!enemy[r[i].v]) {
enemy[r[i].v] = r[i].u;
}
else {
merge(enemy[r[i].v], r[i].u);
}
}
cout << 0 << endl;
return 0;
}
个人感觉二分图比较好想到,二分答案 P1113 是个水题 但还是成功掉坑两次 P1983 实在是妙题 从未经过车站向经过车站连边建图,然后topo一下 更新于2020.3.11 )类似吧 并查集经典好题 食物链
教训(L+1// ConsoleApplication5.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include
#include "pch.h"
#include
TOPO
(说实话是第一次写拓扑排序呢,离散学得很垃圾…
描述一下流程,免得以后忘记了
(1)队列操作
(2)将入度0的点入队,查找与这个点相连的其他点,让他们的入度–,若入度为0,入队,更新层次个数(是这题目里要维护的最大值),直到队列为空,结束。#include
今天在洛谷玩耍 看到noip的初试题(没错就是高中同学说不满分就不能出线的那个笔试orz
我
我考了。
78分。
。好菜
不过学会了一个单调栈求大于某个数的第一个位置(lower bound?
思想是这样的,把第一个数字装入栈中。
每次取出顶上的元素,与下一个元素比较,有两种情况:
若栈顶元素小于下一个元素,那么下一个元素就是大于栈顶元素的第一个元素,为栈顶元素更新答案,出栈(直到栈中的元素大于下一个元素或者栈为空),让下一个元素进栈;
若栈顶元素大于下一个元素,把下一个元素也入栈,这样继续查找,找到的答案一定先满足栈顶元素,再符合栈底元素(因为栈底比栈顶大嘛)。
真的好爱洛谷dalao讲解真的好清楚 很棒//i
#include"pch.h"
#include