NOIP模拟题 2016.11.11 [搜索] [动态规划] [网络流] [字符串处理]

tractor
题目描述
农场上有N(1 <= N <= 50,000)堆草,放在不同的地点上。FJ有一辆拖拉机,也在农场上。拖拉机和草堆都表示为二维平面上的整数坐标,坐标值在1..1000的范围内。拖拉机的初始位置与所有草堆不同。
FJ开拖拉机时,只能平行于坐标轴(即东、南、西、北四个方向),而且每次开动的一段必须是整数长度。
例如,他可以向北开2个单位长度,然后向东开3个单位长度。拖拉机不能开到草堆的位置。
请帮助FJ计算出最少要移动多少个草堆,他才能将拖拉机开回坐标原点。
拖拉机可以开到1..1000之外的地方去。
输入
第1行: 3个整数,即N 和拖拉机的初始位置 (x,y)
第2..1+N行: 每行2个整数,表示一堆草的位置 (x,y)
输出
第1行: FJ必须移动的最少的草堆的数量
样例输入
7 6 3
6 2
5 2
4 3
2 1
7 3
5 4
6 4
样例输出
1
提示
样例说明:拖拉机初始在(6,3),7堆草分别在 (6,2), (5,2), (4,3), (2,1), (7,3), (5,4), (6,4).
FJ必须移动一堆草


搜索,从(0,0)开始bfs,搜索(X,Y),途中经过一个障碍层数就+1,那么分层bfs即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = 1005;
const int MIN = 0;
const int MAX = 1001;
int n,X,Y;
bool g[maxn][maxn];
inline void init()
{
    read(n); read(X),read(Y);
    for(int i=1;i<=n;i++)
    {
        int x,y;
        read(x); read(y);
        g[x][y] = true;
    }
}
struct Node
{
    int x,y;
    Node (const int &_x,const int &_y) { x=_x; y=_y; }
};
inline bool inRange(int x,int y) { return MIN<=x&&x<=MAX && MIN<=y&&y<=MAX; }
const int dx[] = {-1,0,0,1};
const int dy[] = {0,-1,1,0};
queue <Node> que[2];
bool vis[maxn][maxn];
int bfs()
{
    int cur=0,nxt=cur^1;
    int step = -1;
    que[nxt].push(Node(0,0));
    while(!que[cur].empty() || !que[nxt].empty())
    {
        step++;
        swap(cur,nxt);
        while(!que[cur].empty())
        {
            Node now = que[cur].front(); que[cur].pop();
            if(now.x==X && now.y==Y) return step;
            for(int k=0;k<4;k++)
            {
                int x = now.x + dx[k];
                int y = now.y + dy[k];
                if(!inRange(x,y) || vis[x][y]) continue;
                vis[x][y] = true;
                if(g[x][y]) que[nxt].push(Node(x,y));
                else que[cur].push(Node(x,y));
            }
        }
    }
    return -1;
}
int main()
{
    freopen("tractor.in","r",stdin);
    freopen("tractor.out","w",stdout);
    init();
    int ans = bfs();
    printf("%d",ans);
    return 0;
}

Landscaping
题目描述
N(1 <= N <= 100)个数排成一行,值分别为A_i,现在希望把每个数对应地改成B_i。(A_i,B_i的值均在0..10之间)。改变的方式有3种:
(1)把A_i增加到B_i,每增加1,花费 X(2)AiBi1 Y
(3)把第i个数的值转移到第j个数,每转移1,花费为$Z*|i-j|
问:最小的花费是多少。
输入
第1行:4个整数 N, X, Y, Z (0 <= X, Y, Z <= 1000).
第2..1+N行: 每行2个整数 A_i 和 B_i.
输出
第1行:1个整数,表示最小的花费。
样例输入
4 100 200 1
1 4
2 3
3 2
4 0
样例输出
210
提示
INPUT DETAILS: There are 4 flowerbeds in a row, initially with 1, 2, 3, and 4 units of dirt. Farmer John wishes to transform them so they have 4, 3, 2, and 0 units of dirt, respectively. The costs for adding, removing, and transporting dirt are 100, 200, and 1.
OUTPUT DETAILS: One unit of dirt must be removed (from flowerbed #4), at a cost of 200. The remaining dirt can be moved at a cost of 10 (3 units from flowerbed #4 to flowerbed #1, 1 unit from flowerbed #3 to flowerbed #2).


方法一:O(10*n^2)
要减少状态表示,注意到相互转移的代价是 abs(i-j) ,不如一步一步地来,这样就可以用dp(i,j)表示当前处理了前i个数,还需要从后面的数转移j个到i这个地方地最小代价。
转移的时候同一层里面从(i,j)转移到(i,j-1)和(i,j+1)即可,注意下标负数的处理。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = 105;
const int maxd = 2005;
const int base = 1000;
int V;
int a[maxn],b[maxn],delta[maxn];
int n,X,Y,Z;
inline void init()
{
    read(n);
    read(X),read(Y),read(Z);
    for(int i=1;i<=n;i++) read(a[i]),read(b[i]),V+=abs(a[i]-b[i]),delta[i]=b[i]-a[i];
}
int dp[maxn][maxd];
int dynamic()
{
    memset(dp,0x3f,sizeof(dp));
    dp[0][base] = 0;
    for(int i=0;i<n;i++)
    {
        for(int j=-V;j<=V;j++) smin(dp[i+1][base+j+delta[i+1]],dp[i][base+j]+Z*abs(j));
        for(int j=V;j>=1;j--)
        {
            smin(dp[i+1][base+(j-1)],dp[i+1][base+j]+X);
            smin(dp[i+1][base-(j-1)],dp[i+1][base-j]+Y);
        }
    }
    return dp[n][base];
}
int main()
{
    freopen("landscaping.in","r",stdin);
    freopen("landscaping.out","w",stdout);
    init();
    int ans = dynamic();
    printf("%d",ans);
    return 0;
}

方法二:O(100*n^2)
把数组下标标号,两个序列中分别把 i 扩展A[i] B[j]个,然后dp(i,j)表示让AB串前i,j个相同的最小费用,对于每个(i,j),转移到(i,j-1) (i-1,j) (i-1,j-1),分别代表增加一个,减少一个,转移一个。
标程:


#include <stdio.h>
#define INF 2000000000
#define MIN(x,y) ((x)<(y) ? (x) : (y))
#define ABS(x) ((x) > 0 ? (x) : -(x))

int A[1001], B[1001], nA, nB;
int C[1001][1001], X, Y, Z;

int main(void)
{
  int i, j, n;

  freopen ("landscape.in", "r", stdin);
  freopen ("landscape.out", "w", stdout);

  scanf ("%d %d %d %d", &n, &X, &Y, &Z);
  for (i=0; i<n; i++) {
    scanf ("%d", &j); while (j>0) { A[++nA] = i; j--; } 
    scanf ("%d", &j); while (j>0) { B[++nB] = i; j--; } 
  }

  for (j=0; j<=nB; j++) C[0][j] = j*X;
  for (i=0; i<=nA; i++) C[i][0] = i*Y;

  for (i=1; i<=nA; i++)
    for (j=1; j<=nB; j++) {
      C[i][j] = INF;
      C[i][j] = MIN(C[i][j], C[i][j-1] + X);
      C[i][j] = MIN(C[i][j], C[i-1][j] + Y);
      C[i][j] = MIN(C[i][j], C[i-1][j-1] + Z * ABS(A[i]-B[j]));
    }

  printf ("%d\n", C[nA][nB]);
  return 0;
}

方法三:O(kn^2*MAX)
也可以用网络流,S代表需要加,T代表需要减,分成二部图,S与i相连(X,INF),i*与T相连(Y,INF),中间任意两点连边(Z*abs(i-j),delta(i)),然后MCMF即可。


equal
【问题描述】
明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。
这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?
这个选择题中的每个表达式都满足下面的性质:
1. 表达式只可能包含一个变量‘a’。
2. 表达式中出现的数都是正整数,而且都小于10000。
3. 表达式中可以包括四种运算‘+’(加),‘-’(减),‘’(乘),‘^’(乘幂),以及小括号‘(’,‘)’。小括号的优先级最高,其次是‘^’,然后是‘’,最后是‘+’和‘-’。‘+’和‘-’的优先级是相同的。相同优先级的运算从左到右进行。(注意:运算符‘+’,‘-’,‘*’,‘^’以及小括号‘(’,‘)’都是英文字符)
4. 幂指数只可能是1到10之间的正整数(包括1和10)。
5. 表达式内部,头部或者尾部都可能有一些多余的空格。
下面是一些合理的表达式的例子:
((a^1) ^ 2)^3,a*a+a-a,((a+a)),9999+(a-a)*a,1 + (a -1)^3,1^10^9……
【输入文件】
输入文件equal.in的第一行给出的是题干中的表达式。第二行是一个整数n(2 <= n <= 26),表示选项的个数。后面n行,每行包括一个选项中的表达式。这n个选项的标号分别是A,B,C,D……
输入中的表达式的长度都不超过50个字符,而且保证选项中总有表达式和题干中的表达式是等价的。
【输出文件】
输出文件equal.out包括一行,这一行包括一系列选项的标号,表示哪些选项是和题干中的表达式等价的。选项的标号按照字母顺序排列,而且之间没有空格。
【样例输入】
( a + 1) ^2
3
(a-1)^2+4*a
a + 1+ a
a^2 + 2 * a * 1 + 1^2 + 10 -10 +a -a
【样例输出】
AC
【数据规模】
对于30%的数据,表达式中只可能出现两种运算符‘+’和‘-’;
对于其它的数据,四种运算符‘+’,‘-’,‘*’,‘^’在表达式中都可能出现。
对于全部的数据,表达式中都可能出现小括号‘(’和‘)’。


类似于NOIP2014解方程,带入a和mod,判断是否都一样即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int maxn = 15;
const int N = 10;
const int MOD[maxn] = {0,25243,25657,29077,29101,32257,32303,34213,34843,35279,38197};
const int A[maxn] = {0,18521,18917,22153,22541,24907,27541,30203,30853,32077,33091};
int VAL[maxn];
const int maxlen = 55;
char s[maxlen];
int lens;
void readstring()
{
    lens = 0;
    char ch = (char) getchar();
    while(ch=='\n' || ch==' ') ch = (char) getchar();
    while(ch!='\n' && ch!=EOF)
    {
        if(ch!=' ') s[++lens]=ch;
        ch = (char) getchar();
    }
    s[0] = '('; s[++lens] = ')';
}
int quick_exp(int a,int p,int mod)
{
    if(!p) return 1;
    int tmp = quick_exp(a,p>>1,mod);
    tmp = tmp*tmp%mod;
    if(p&1) return tmp*a%mod;
    else return tmp;
}
inline int prior(char ch,bool side)
{
    switch(ch)
    {
        case '+':
        case '-': return side? 2 : 1;
        case '*': return side? 4 : 3;
        case '^': return side? 6 : 5;
        case '(': return side? 0 : 8;
        case ')': return side? 8 : 0;
        default: return -1;
    }
}
stack <char> sym;
stack <int> num;
void calc(int mod)
{
    char op = sym.top(); sym.pop();
    int t2 = num.top(); num.pop();
    int t1 = num.top(); num.pop();
    switch(op)
    {
        case '+': num.push((t1+t2)%mod); break;
        case '-': num.push((t1-t2)%mod); break;
        case '*': num.push(t1*t2%mod); break;
        case '^': num.push(quick_exp(t1,t2,mod)); break;
    }
}
int expression(int a,int mod)
{
    while(!num.empty()) num.pop();
    while(!sym.empty()) sym.pop();
    int it = 0;
    while(it <= lens)
    {
        int x = 0;
        bool isnum = false;
        if(s[it]=='a') x=a,isnum=true,it++;
        else while(s[it]>='0' && s[it]<='9') (x*=10)+=(s[it++]-'0'),isnum=true;
        if(isnum) num.push(x);
        if(it > lens) break;
        if(s[it]=='-' && s[it-1]=='(') num.push(0);
        while(!sym.empty() && prior(sym.top(),true) > prior(s[it],false)) calc(mod);
        if(s[it]==')') sym.pop();
        else sym.push(s[it]);
        it++;
    }
    return (num.top()%mod+mod)%mod;
}
int main()
{
    freopen("equal.in","r",stdin);
    freopen("equal.out","w",stdout);
    readstring();
    for(int k=1;k<=N;k++) VAL[k] = expression(A[k],MOD[k]);
    int T;
    read(T);
    for(int i=1;i<=T;i++)
    {
        readstring();
        bool flag = true;
        for(int k=1;k<=N;k++)
        {
            int tmp = expression(A[k],MOD[k]);
            if(tmp ^ VAL[k]) { flag=false; break; }
        }
        if(flag) putchar(i+'A'-1);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值