PKUSC前恢复性训练

联赛之后没怎么碰过OI了..

为了PKUSC...只能捡起来了..

那么..我们从零开始吧..


DAY1 搜索题恢复性训练

POJ 2386 傻逼题八连块

POJ 1979 傻逼题走迷宫

POJ 2251 3D广搜 打起来还蛮熟练的.

CodeVS 迷宫 广搜模版


DAY2 贪心恢复性训练

POJ 3617 大概是求个字典序最小 首位不同时取小 相同时观察下一位的字典序必有最优解

CodeVS 1214 右端点排序 可以证明完成先结束的任务一定更优(不会更差)

POJ 3069 一开始没看懂题 应该是滚动区间每次取区间最右

POJ 3253 可以证明最短的和第二短的应该是二叉树上的兄弟节点 递归就好了

POJ 1328 诶劳资今天还就跟你这题刚上了 网上搜的所有数据都能过题库居然WA了 估计明天睡一觉起来可能就发现问题了吧..

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 1000 + 10;

int n, d, tot = 1;
struct data1 { double x, y; }point[MAXN];
struct data2 { double left, right; }range[MAXN];
inline bool cmp(data2 a, data2 b) { return a.left < b.left; }

int main() {
	while( scanf("%d%d", &n, &d) && (n || d) ) {
		bool flag = true;
		for(int i = 1; i <= n; ++i) {
			scanf("%lf%lf", &point[i].x, &point[i].y);
			if(point[i].y > d) flag = false;
			range[i].left = point[i].x - sqrt(d * d - point[i].y * point[i].y);
			range[i].right = point[i].x + sqrt(d * d - point[i].y * point[i].y);
		}
		if(!flag) { printf("Case %d: -1\n", tot++); continue; }
		sort(range + 1, range + n + 1, cmp);
		double rightnow = range[1].right; //int
		int ans = 1;
		for(int i = 2; i <= n; ++i) {
			if(range[i].left < rightnow && range[i].right > rightnow) continue;
			else if(range[i].left > rightnow) { ans++; rightnow = range[i].right; }
			else if(range[i].left < rightnow && range[i].right < rightnow) { rightnow = range[i].right; }
		}
		printf("Case %d: %d\n", tot++, ans);
	}
	return 0;
}

思路就是个计算几何+贪心 讨论3种情况取最优

Update:错误地方已经找到了 加注

不得不说半年不打oi 整个人都变傻逼了



DAY3 DP恢复性训练

背包、LCS、LIS(nlogn)已打

POJ 1160 划分dp,设dp[i][j]表示前i个村庄建j个邮局的最小代价和,划分成前k个村庄建j-1个和k+1到i建一个,由于村庄顺序排列所以易证中位数为最小代价点

POJ 1742 之前在蓝人8题上找到的题,写了两发多重背包全T了,于是准退役狗又得学点新姿势了..

用dp[i][j]表示凑成面值j的情况下第i种硬币的剩余数量,那么我们有:

若dp[i-1][j]>=0,则dp[i][j]显然等于C[i]

若j

其余的就是递推了 dp[i][j]=dp[i][j-A[i]]-1

#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 100 + 10;
const int MAXM = 100000 + 50;

int N, M;
int A[MAXN], C[MAXN];
int dp[MAXM];

int main() {
	while( scanf("%d%d", &N, &M) && (N || M) ) {
		memset(dp, -1, sizeof(dp)); dp[0] = 0;
		int cnt = 0;
		for(int i = 1; i <= N; ++i) scanf("%d", &A[i]);
		for(int i = 1; i <= N; ++i) scanf("%d", &C[i]);
		for(int i = 1; i <= N; ++i) for(int j = 0; j <= M; ++j) {
			if(dp[j] >= 0) dp[j] = C[i]; //注意这里的第一个dp[j]表示dp[i-1][j],因为是从上一层过来的 
			else if(j < A[i]) dp[j] = -1;
			else dp[j] = dp[j - A[i]] - 1;
		}
		for(int i = 1; i <= M; ++i) if(dp[i] >= 0) cnt++;
		printf("%d\n", cnt);
    }
    return 0;
}

POJ 2229 现在菜的这种题都要想半天了,日哦

当n为奇数时,唯一的变化就是在n-1上加个1

当n为偶数时,可以从n-2加2个1过来或者加1个2过来,加1个2的方案数其实就是n/2的方案数

所以dp[i] = (i % 2 ? dp[i - 1] : dp[i - 2] + dp[i >> 1])

POJ 2385 水题,顺便感觉现在的代码风格好毒瘤啊.. 感受一下

#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 1000 + 10;
const int MAXW = 30 + 10;

int T, W;
int apple[MAXN];
int dp[MAXN][MAXW];

int main() {
	scanf("%d%d", &T, &W);
	for(int i = 1; i <= T; ++i) scanf("%d", &apple[i]);
	dp[1][0] += (apple[1] == 1);
	dp[1][1] += (apple[1] == 2);
	for(int i = 2; i <= T; ++i) dp[i][0] = dp[i - 1][0] + (apple[i] == 1);
	for(int i = 2; i <= T; ++i) for(int j = 0; j <= W; ++j) {
		dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + (apple[i] == j % 2 + 1);
	}
	int ans = -0x3f3f3f3f;
	for(int i = 0; i <= W; ++i) ans = max(ans, dp[T][i]);
	printf("%d\n", ans);
	return 0;
}


POJ 3046 这题尼玛长姿势了...WA了一下午才发现是统计答案的时候统计错了

以为是简单DP结果敲了个复杂度O(T*S^2 + T*B^2)上去T成智障

看了下题解是滚动数组可做,不过速度不是很乐观

看了下挑战程序设计P69 这题的类型叫多重集组合数 然后有个很有趣的结论

PKUSC前恢复性训练_第1张图片

顺便这个结论代码敲出来似乎用不着滚动数组...空间给的很大MLE不了

#include 
#include 
#include 
#include 
using namespace std;

const int MAXT = 1000 + 10;
const int MAXA = 100000 + 50;
const int MOD = 1000000;

int T, A, S, B;
int dp[MAXT][MAXA];
int tot[MAXT];

int main() {
    scanf("%d%d%d%d", &T, &A, &S, &B);
    for(int i = 0, cnt; i < A; ++i) {
        scanf("%d", &cnt);
        tot[cnt]++;
    }
    for(int i = 0; i <= T; ++i) dp[i][0] = 1;
    for(int i = 0; i < T; ++i) for(int j = 1; j <= B; ++j) {
        if(j - 1 - tot[i + 1] >= 0) dp[i + 1][j] = (dp[i + 1][j - 1] + dp[i][j] - dp[i][j - 1 - tot[i + 1]] + MOD) % MOD;
        else dp[i + 1][j] = (dp[i + 1][j - 1] + dp[i][j]) % MOD;
    }
    int ans = 0;
    for(int i = S; i <= B; ++i) ans += dp[T][i], ans %= MOD;
    printf("%d\n", (ans + MOD) % MOD);
    return 0;
}

DAY4 基础数据结构恢复性训练

今天POJ挂了...还好百练没挂

POJ 2431 这题目测可以直接模拟,不过优先队列是最优解法。注意题目给的坐标是到终点的所以得倒过来排序一下

POJ 3614 水题,优先队列维护满足minSPFi

POJ 2010 奶大招生系列...等等这句话怎么怪怪的先把成绩从高到低排列然后用优先队列维护所需费用,找第一个>=F的即可

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 100000 + 50;

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}

int N, C, F;
int lower[MAXN], upper[MAXN];
struct data { int score, fee; }cow[MAXN];
inline bool cmp(data a, data b) { return a.score > b.score; }

int main() {
	N = read(), C = read(), F = read();
	int mid = (N - 1) >> 1;
	for(int i = 0; i < C; ++i) cow[i].score = read(), cow[i].fee = read();
	sort(cow, cow + C, cmp);
	priority_queue que;
	int tot1 = 0;
	for(int i = 0; i < C; ++i) {
		if(i < mid) que.push(cow[i].fee), tot1 += cow[i].fee;
		else {
			lower[i] = tot1;
			if(cow[i].fee <= que.top()) {
				tot1 -= que.top(); tot1 += cow[i].fee;
				que.pop(); que.push(cow[i].fee);
			}
		}
	}
	int tot2 = 0;
	for(int i = C - 1; i >= 0; --i) {
		if(i > C - mid - 1) que.push(cow[i].fee), tot2 += cow[i].fee;
		else {
			upper[i] = tot2;
			if(cow[i].fee <= que.top()) {
				tot2 -= que.top(); tot2 += cow[i].fee;
				que.pop(); que.push(cow[i].fee);
			}
		}
	}
	int ans = -1;
	for(int i = C - mid - 1; i >= mid; --i) if(lower[i] + upper[i] + cow[i].fee <= F)
	    ans = cow[i].score;
	printf("%d\n", ans);
	return 0;
}

POJ 1182 挑战程序设计讲的很清楚,思路有点类似于2-SAT,这里做完整摘录(当然,这么做速度似乎不如带权并查集)

对于每只动物i创建3个元素i-A,i-B,i-C,并用这3*N个元素建立并查集。这个并查集维护如下信息:

■i-x表示“i属于种类x”。

■并查集里的每一个组表示组内所有元素代表的情况都同时发生或不发生。

例如,如果i-A和j-B在同一个组里,就表示如果i属于种类A那么j一定属于种类B,如果j属于种类B那么i一定属于种类A。因此,对于每一条信息,只需要按照下面进行操作就可以了。

■第一种,x和y属于同一种类····合并x-A和y-A、x-B和y-B、x-C和y-C。

■第二种,x吃y··········合并x-A和y-B、x-B和y-C,x-C和y-A。

不过在合并之前,需要先判断合并是否会产生矛盾。例如在第一种信息的情况下,需要检查比如x-A和y-B或者y-C是否在同一组等信息。

#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 150000 + 50;
const int MAXK = 100000 + 50;

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}

int par[MAXN], rank[MAXN];

void init(int n) { for(int i = 1; i <= n; ++i) par[i] = i, rank[i] = 0; }

inline int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); }

inline void unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return;
    if(rank[x] < rank[y]) par[x] = y;
    else {
        par[y] = x;
        if(rank[x] == rank[y]) rank[x]++;
    }
}

inline bool same(int x, int y) { return find(x) == find(y); }

int N, K, ans;
int D[MAXK], X[MAXK], Y[MAXK];

void solve() {
	init(N * 3); int ans = 0;
	for(int i = 1; i <= K; ++i) {
		int d = D[i], x = X[i], y = Y[i];
		if(x < 1 || x > N || y < 1 || y > N) { ans++; continue; }
		if(d == 1) {
			if(same(x, y + N) || same(x, y + (N << 1))) ans++;
			else unite(x, y), unite(x + N, y + N), unite(x + (N << 1), y + (N << 1));
		}
		else {
			if(same(x, y) || same(x, y + (N << 1))) ans++;
			else unite(x, y + N), unite(x + N, y + (N << 1)), unite(x + (N << 1), y);
		}
	}
	printf("%d\n", ans);
}

int main() {
	N = read(), K = read();
	for(int i = 1; i <= K; ++i) { D[i] = read(), X[i] = read(), Y[i] = read(); }
	solve();
	return 0;
}

POJ 1703 上边那题的思路弱弱弱化版,水题


DAY5 图论恢复性训练(上)

最短路模板整理 http://blog.csdn.net/sci_m3/article/details/71950633

LUOGU 1330 二分图判断 水题 就当复习链式前向星了

#include 
#include 
#include 
using namespace std;

const int MAXN = 10000 + 10;
const int MAXM = 100000 + 10;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

struct node { int to, next; }edge[MAXM];
int n, m, cnt, head[MAXN], tot[2], color[MAXN], ans;
bool vis[MAXN];

inline void addedge(int u, int v) {
    edge[++cnt].to = v; edge[cnt].next = head[u];
    head[u] = cnt;
}

bool dfs(int u, int col) {
    vis[u] = true; color[u] = col; tot[col]++;
    for(int i = head[u]; i; i = edge[i].next) {
        int v = edge[i].to;
        if(vis[v] && color[u] == color[v]) return false;
        else if(!vis[v] && !dfs(v, col ^ 1)) return false; 
    }
    return true;
}

int main() {
    n = read(), m = read();
    for(int i = 1, u, v; i <= m; ++i) {
        u = read(), v = read();
        addedge(u, v); 
        addedge(v, u);
    }
    for(int i = 1; i <= n; ++i) if(!vis[i]) {
        vis[i] = true;
        tot[0] = tot[1] = 0;
        if(!dfs(i, 1)) {
            puts("Impossible");
            return 0;
        }
        ans += min(tot[0], tot[1]);
    }
    printf("%d\n", ans);
    return 0;
}
POJ和百练今天全挂,断更。


DAY5 图论恢复性训练(下

大概是定期维护反正POJ和Openjudge大概会一直挂下去了...只能用LUOGU将就一下了..

最小生成树模板整理 http://blog.csdn.net/sci_m3/article/details/72123169

LUOGU 1462 很有营养的一题,最短路中有两个权值,保证其中一个权值的最大值最小,一看就是二分

二分最大花费的最小值,根据血量跑SPFA,如果血量还有剩余说明还有能力让花费更小

#include 
#include 
#include 
#include 
#include 
#define LL long long
using namespace std;

const int MAXN = 10000 + 50;
const int MAXM = 50000 + 50;
const int inf = 0x3f3f3f3f;

inline LL read() {
	LL x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}

LL n, m, b;
LL f[MAXN], dis[MAXN];
bool vis[MAXN];
struct node { LL to, next, w; }edge[MAXM << 1];
int head[MAXN], cnt;

inline void addedge(int u, int v, int w) {
	edge[++cnt].to = v, edge[cnt].next = head[u];
	edge[cnt].w = w; head[u] = cnt;
}

inline bool SPFA(int x) {
	queue Q;
	memset(vis, false, sizeof(vis));
	memset(dis, inf, sizeof(dis));
	for(int i = 2; i <= n - 1; ++i) vis[i] = (f[i] > x);
	if(f[1] > x || f[n] > x) return false;
	dis[1] = 0; Q.push(1);
	while(!Q.empty()) {
		LL u = Q.front(); Q.pop();
		vis[u] = false;
		for(LL i = head[u]; i; i = edge[i].next) {
			LL v = edge[i].to;
			if(dis[u] + edge[i].w < dis[v]) {
				dis[v] = dis[u] + edge[i].w;
				if(!vis[v]) vis[v] = true, Q.push(v);
			}
		}
	}
	return dis[n] <= b;
}

int main() {
	n = read(), m = read(), b = read();
	LL l = 1, r = -1, ans;
	for(int i = 1; i <= n; ++i) f[i] = read(), r = max(f[i], r);
	for(int i = 1, u, v, w; i <= m; ++i) {
		u = read(), v = read(), w = read();
		addedge(u, v, w); addedge(v, u, w);
	}
	while(l <= r) {
		LL mid = (l + r) / 2;
		if(SPFA(mid)) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	if(!SPFA(inf)) puts("AFK\n");
	else printf("%lld\n", ans);
	return 0;
}


你可能感兴趣的:(动态规划,搜索,贪心,数据结构,图论)