洛谷快乐学习

P 2016 战略游戏
先喂一波题意:给一颗树,选最小点覆盖(选定一个点,与它相连的那些点也将被覆盖)

1. 树形dp

#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;
}

2. 贪心

秀无敌啊
策略 如下:从叶子开始处理,对每一个正在处理的节点,查看它是否被点亮,如果未被点亮,点亮它的父亲(点父亲总不会比点亮自己差,父亲可能连接更多的点,如果已经被点亮,就暂时不需点亮它的父亲(不一定需要),再看它父亲的其他节点;当发现它父亲的儿子已经全被点亮而它父亲还未被点亮,将父亲放入队列后续判断。
最后统计点亮个数即可。

#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;
} 

3 匈牙利算法

小结论:

最小覆盖数==最大匹配数(如果是无向图 == 最大匹配数/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
两种做法-

1. 并查集

并查集带权,实现基于贪心的思想。把冲突值递减排列,尽量把值大的两个人划分到不同的监狱。有一个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;
}

2 二分图

个人感觉二分图比较好想到,二分答案
教训(L+1

// ConsoleApplication5.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5 + 5;
int N, M, R;
struct P {
 int nxt, to, w;
 bool operator <(const P r) {
  return w > r.w;
 }
};
int head[maxn*2],cnt;
P edge[maxn * 2];
void addedge(int u, int v, int w) {
 edge[++cnt].w = w;
 edge[cnt].nxt = head[u];
 edge[cnt].to = v;
 head[u] = cnt;
}
//搭个架子先,这里是二分染色咯
bool test(int x) {
 queue <int> q;
 //标记颜色
 int color[maxn*2] = {};
 for (int i = 1;i <= N;i++) {
  if (!color[i]) {
   q.push(i);
   color[i] = 1;
   while (!q.empty()) {
    int pp = q.front();
    q.pop();
    for (int j = head[pp];j;j = edge[j].nxt){
     int qq = edge[j].to;
     if (edge[j].w >= x) {
      if (!color[qq]) {
       q.push(qq);
       if (color[pp] == 1)
        color[qq] = 2;
       else color[qq] = 1;
      }
      else if (color[pp] == color[qq])
       return false;
     }
    }
   }
  }
 }
 return true;
}
int main() {
 cin >> N >> M;
 for (int i = 1;i <= M;i++) {
  int u, v, w;
  cin >> u >> v >> w;
  addedge(u, v, w);
  addedge(v, u, w);
  R = max(R, w);
 }
 int L = 0;
 R++;
 //R++???????????
 while (L +1 < R) {
  int mid = (L+R)>>1;
  //如果可以,缩小范围
  if (test(mid)) R = mid;
  //不行,扩大
  else L = mid;
 }
 cout << L << endl;
 return 0;
}

P1113 是个水题 但还是成功掉坑两次

#include "pch.h"
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e4 + 5;
int n,degree[maxn],ans,l[maxn],tim[maxn];
vector<int> v[maxn];
void topo() {
 queue<int> q;
 for (int i = 1;i <= n;i++) {
  if (degree[i] == 0) {
   q.push(i);
   tim[i] = l[i];
  }
 }
 while (!q.empty()) {
  int u = q.front();
  q.pop();
  for (int i = 0;i < v[u].size();i++) {
   int nxt = v[u][i];
   degree[nxt]--;
   if (degree[nxt] == 0) {
    q.push(nxt);
   }
   tim[nxt] = max(tim[u] + l[nxt], tim[nxt]);
  }
 }
 for (int i = 1;i <= n;i++)
  ans = max(ans, tim[i]);
 
}
int main() {
 cin >> n;
 for (int i = 1;i <= n;i++) {
  int id, x,len;
  cin >> id >> len;
  l[id] = len;
  while (cin >> x && x) {
   degree[id]++;
   v[x].push_back(id);
  }
 }
 topo();
 cout << ans << endl;
 return 0;
}

P1983 实在是妙题

TOPO

从未经过车站向经过车站连边建图,然后topo一下
(说实话是第一次写拓扑排序呢,离散学得很垃圾…
描述一下流程,免得以后忘记了
(1)队列操作
(2)将入度0的点入队,查找与这个点相连的其他点,让他们的入度–,若入度为0,入队,更新层次个数(是这题目里要维护的最大值),直到队列为空,结束。

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5 + 5;
//topo排序
int degree[maxn],n,m,ans;
bool vis[maxn];
vector<int> v[maxn];
struct P {
 int id, cnt, book[1005];
}s[1005];
bool edge[1005][1005];
void topo() {
 queue<pair<int, int> > q;
 for (int i = 1;i <= n;i++) {
  if (degree[i] == 0) {
   q.push(make_pair(i,1));
  }
 }
 ans = 1;
 while (!q.empty()) {
  int u = q.front().first, val =q.front().second ;
  q.pop();
  for (int i = 0;i < v[u].size();i++) {
   int nxt = v[u][i];
  // cout << "Pass by " << nxt << endl;
   degree[nxt]--;
   if (degree[nxt] == 0) {
    q.push(make_pair(nxt, val + 1));
   // cout << "val + 1 = " << val + 1<< endl;
    ans = max(ans, val + 1);
   }
  }
 }
}
int tmp[maxn];
void ini() {
 for (int i = 1;i <= n;i++) 
  vis[i] = false;
}
int main() {
 cin >> n >> m;
 for (int i = 1;i <= m;i++) {
  ini();
  cin >> s[i].cnt;
  for (int j = 1;j <= s[i].cnt;j++) {
   //第i列火车的第j个停靠点
   int x; cin >> x;
   s[i].book[j] = x;
   //站点标记,代表这是此次列车的一个停靠点
   vis[x] = true;
  }
  //此次经过的所有站点(始发站 -> 终点站)
  for (int j = s[i].book[1];j <= s[i].book[s[i].cnt];j++) {
   //若无停靠,入为0
   if (vis[j]) continue;
   for (int k = 1;k <= s[i].cnt;k++) {
    //如果边标记过了,就无需标记,所以这个edge数组是不需要每次清空的
    //因为多趟列车,所以会有重复的
    if (!edge[j][s[i].book[k]]) {
     degree[s[i].book[k]]++;
     v[j].push_back(s[i].book[k]);
     edge[j][s[i].book[k]] = 1;
    }
   }
  }
 }
 topo();
 cout << ans << endl;
 return 0;
}

更新于2020.3.11
今天在洛谷玩耍 看到noip的初试题(没错就是高中同学说不满分就不能出线的那个笔试orz

我考了。
78分。
。好菜
不过学会了一个单调栈求大于某个数的第一个位置(lower bound?

)类似吧
思想是这样的,把第一个数字装入栈中。
每次取出顶上的元素,与下一个元素比较,有两种情况:
若栈顶元素小于下一个元素,那么下一个元素就是大于栈顶元素的第一个元素,为栈顶元素更新答案,出栈(直到栈中的元素大于下一个元素或者栈为空),让下一个元素进栈;
若栈顶元素大于下一个元素,把下一个元素也入栈,这样继续查找,找到的答案一定先满足栈顶元素,再符合栈底元素(因为栈底比栈顶大嘛)。

并查集经典好题 食物链
真的好爱洛谷dalao讲解真的好清楚 很棒

//i
#include"pch.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define pi pcos(-1.0)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mp make_pair
using namespace std;
const int maxn = 3e5 + 5;
int n, k,f[maxn],ans;
int find(int x) {
 return f[x] == x ? x : f[x] = find(f[x]);
}
int main() {
 cin >> n >> k;
 for (int i = 1;i <= 3*n;i++) {
  f[i] = i;
 }
 for (int i = 1;i <= k;i++) {
  int flag, u, v; cin >> flag >> u >> v;
  if (u > n || v > n ) { ans++;continue; }
  if (flag == 1) {
   //同类 如果存在吃与被吃的关系,假
   if (find(u + n) == find(v) || find(u) == find(v + n))
    ans++;
   else {
    f[find(u)] = find(v);
    f[find(u + n)] = find(v + n);
    f[find(u + n + n)] = find(v + n + n);
   }
  }
  else {
   //u吃v,如果存在反吃关系或同类关系,假
   if (find(v) == find(u+n) || find(u) == find(v))
    ans++;
   else {
    f[find(v + n)] = find(u);
    f[find(v + n + n)] = find(u + n);
    f[find(v)] = find(u + n + n);
   }
  }
 }
 cout << ans << endl;
 return 0;
}

你可能感兴趣的:(ACM-乱七八糟)