NOIP2017(普及组) 题解

T1

#include 
using namespace std;

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    cout << a/5 + b/10*3+c/2 << endl;
    return 0;
}


T2

我用的是字符串,其实用模运算更好

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

struct Bok {
    string name;
    int l;
};
Bok Book[5001];

int n, q;
int Bk[5001];
string Numb[5001];

int main() {
    int len;
    cin >> n >> q;
    for(int i=1; i<=n; i++) {
        cin >> Bk[i];
    }
    sort(Bk+1, Bk+n+1);
    for(int i=1; i<=n; i++) {
        stringstream turn;
        turn << Bk[i];
        turn >>  Book[i].name;
        Book[i].l = Book[i].name.size();
    }
    bool fod;
    for(int i=1; i<=q; i++) {
        fod = 0;
        cin >> len;
        cin >> Numb[i];
        for(int j=1; j<=n; j++) {
            bool Include = false;
            int indx;
            for(int k=0; k


T3

记忆化搜索算是一种简单的做法

#include 
#include 
using namespace std;

#define MAX 1e9

int m, n;
int Chess[1001][1001]; //棋盘,0表示无色 

int dir[4][2] = {{0,1},{1,0},{-1,0},{0,-1}};
int f[1001][1001]; //记忆化:f[i][j]表示到(i, j) 的最小花费 

void DFS(int a, int b, int sum, bool can_flog) { //坐标(a,b), 花费-sum, 能否使用魔法-can_flog 
    if(a < 1 || a > m || b < 1 || b > m) return;
    if(sum >= f[a][b]) return; //根据记忆化 剪枝 
    f[a][b] = sum;
    if(a == m && b == m) return;
    for(int i=0; i<4; i++) {
        int next_x = a + dir[i][0];
        int next_y = b + dir[i][1];
        if(next_x < 1 || next_x > m || next_y < 1 || next_y > m) continue;
        if(Chess[next_x][next_y] == 0) {
            if(!can_flog) continue;
            Chess[next_x][next_y] = Chess[a][b]; //把下一个格子变为当前格子的颜色 
            DFS(next_x, next_y, sum + 2, false); //不能再用魔法了,can_flog = false 
            Chess[next_x][next_y] = 0; //记得回溯 
        } else { 
            if(Chess[next_x][next_y] != Chess[a][b]) DFS(next_x, next_y, sum + 1, true);
            else if(Chess[next_x][next_y] == Chess[a][b]) DFS(next_x, next_y, sum, true);
        }
    }

}
int main() {
    int x, y, color;
    cin >> m >> n;
    for(int i=1; i<=m; i++)
        for(int j=1; j<=m; j++)
            f[i][j] = MAX;
    for(int i=1; i<=n; i++) {
        cin >> x >> y >> color;
        Chess[x][y] = color + 1;
    }
    DFS(1, 1, 0, true);
    if(f[m][m] == MAX) f[m][m] = -1; //无法到达 
    cout << f[m][m] << endl;
    return 0;
}


T4

正解是二分答案 + DP + 单调队列优化

先看框架

typedef long long LL;
#define MIN -9999999999

struct Data {
	LL pos;
	LL value;
} A[500010];
LL N, D, K;

LL DP[500010];
inline LL max(LL a, LL b) {
	return (a > b) ? a : b;
}

int main() {
	LL sum = 0;
	scanf("%lld%lld%lld", &N, &D, &K);
	for(LL i=1; i<=N; i++) {
		scanf("%lld%lld", &A[i].pos, &A[i].value);
		if(A[i].value > 0) sum += A[i].value;
	}
	if(sum < K) {
		printf("-1\n");
		return 0;
	}
	LL l = 0, r = A[N].pos, mid, ans;
	while(l <= r) {
		mid = (l + r) / 2;
		if(Check(mid)) r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	printf("%lld\n", ans);
	return 0;
}

二分 + DP的Check函数(50分)

DP的思路是:循环j找到之前可以跳到这个格子的点,找到最大的DP_j,则DP_i  = DP_j + 获得的分数(A[i].value)

找不到,则DP_i = -1。

bool Check_Without_Queue(LL m) {
	LL mind = max(1, D-m), maxd = D + m;
	LL ans = 0;
	memset(DP, 0, sizeof(DP));
	for(LL i=1; i<=N; i++) {
		LL mx = 0, k = -1;
		for(LL j=i-1; j>=0; j--) {
			if(A[i].pos - A[j].pos > maxd) break;
			if(DP[j] != -1 && A[i].pos - A[j].pos >= mind) {
				if(mx <= DP[j]) mx = DP[j], k = j;
			}
		}
		if(k == -1) DP[i] = -1;
		else DP[i] = DP[k] + A[i].value, ans = max(ans, DP[i]);
	}
	if(ans >= K) return true;
	return false;
}


单调队列优化的思路大概是:算出最大最小弹跳距离maxd和mind。用一个Deque(双向队列),在DP 格子i之前,加入距离>=mind的点。注意,这里的加入是加入单调队列,因此那些不优的点(DP值小的)应该剔除之后再加入队尾。下一步,从队首剔除>maxd的点,因为如果格子i跳不到,后面的格子i+1,i+2..一定还是跳不到,距离在不断变大。然后进行状态转移。


【Code】连着AC程序一块给了

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

typedef long long LL;
#define MIN -9999999999

struct Data {
	LL pos;
	LL value;
} A[500010];
LL N, D, K;

LL DP[500010];
deque Q;

inline LL max(LL a, LL b) {
	return (a > b) ? a : b;
}

bool Check(LL m) {
	while(!Q.empty()) Q.pop_back();
	memset(DP, 0, sizeof(DP));
	LL mind = max(1, D-m), maxd = D+m;
	LL now = 0;
	A[0].pos = 0;
	for(LL i=1; i<=N; i++) {
		for(;now= mind; now++) {
			while(!Q.empty() && DP[now] >= DP[Q.back()]) Q.pop_back();
			if(DP[now] != -1) Q.push_back(now);
		}
		while(!Q.empty() && A[i].pos - A[Q.front()].pos > maxd) Q.pop_front();
		DP[i] = Q.empty() ? -1 : DP[Q.front()] + A[i].value; 
		if(DP[i] >= K) return true;
	}
	return false;
}

int main() {
	LL sum = 0;
	scanf("%lld%lld%lld", &N, &D, &K);
	for(LL i=1; i<=N; i++) {
		scanf("%lld%lld", &A[i].pos, &A[i].value);
		if(A[i].value > 0) sum += A[i].value;
	}
	if(sum < K) {
		printf("-1\n");
		return 0;
	}
	LL l = 0, r = A[N].pos, mid, ans = -1;
	while(l <= r) {
		mid = l+r >> 1;
		if(Check(mid)) r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	printf("%lld\n", ans);
	return 0;
}


你可能感兴趣的:(NOIP2017(普及组) 题解)