T1:1. 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必须移动一堆草
一层一层扫描,,bfs时,进入一层时在一个队列后加一个标志,这样每一层扫完就可以判断是否是最后一层了,记录了一个边缘点更新的tot,,然后缩小了范围(其实也可以不要这些)
然而愚昧的我竟然把一个==写成了=,QAQ伤心,,,考试时交卷前一定要用Dev编译一下。。。。。。
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,127,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define maxx(x1,x2,x3) max(x1,max(x2,x3))
#define minn(x1,x2,x3) min(x1,min(x2,x3))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "tractor"
#define C(a,b) next_permutation(a,b)
#define LL long long
#define smin(x,tmp) x=min(x,tmp)
#define smax(x,tmp) x=max(x,tmp)
using namespace std;
//===========================================================================
const int N = 1000;
inline int read()
{
int res=0;
char ch=getchar();
while(ch>'9'||ch<'0')ch=getchar();
while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
return res;
}
int mp[N+10][N+10],f[N+10][N+10],Sx,Sy,ANS,n,Rx1,Rx2,Ry1,Ry2,W;
bool bo[N+10][N+10];
const int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
bool inrang(int x,int y){if(x>=Rx1&&x<=Rx2&&y>=Ry1&&y<=Ry2)return true;return false;}
int spfa(int sx,int sy)
{
queue<int>qx;
queue<int>qy;
qx.push(sx),qy.push(sy);
qx.push(-1);
maxcle(f);
f[sx][sy]=0;
bo[sx][sy]=true;
int ans=INF;
int tot=0;
while(!qx.empty())
{
int x=qx.front();qx.pop();
if(x!=-1)
{
int y=qy.front();qy.pop();
for(int i = 0; i <4; i++)
{
int vx=x+dx[i],vy=y+dy[i];
if(inrang(vx,vy)&&f[vx][vy]>f[x][y]+mp[vx][vy])
{
f[vx][vy]=f[x][y]+mp[vx][vy];
if(vx==Rx1||vx==Rx2||vy==Ry1||vy==Ry2)
{
smin(ans,f[vx][vy]);
if(!ans)return ans;
}
if(!bo[vx][vy])
{
bo[vx][vy]=true;
qx.push(vx),qy.push(vy);
if(vx==Rx1||vx==Rx2||vy==Ry1||vy==Ry2)tot++;
}
}
}
}
else
{
if(tot==W)break;
qx.push(-1);
}
}
return ans;
}
void init()
{
n=read(),Sx=read(),Sy=read();
Rx1=N,Rx2=1,Ry1=N,Ry2=1;
for(int i = 1; i <= n; i++)
{
int x=read(),y=read();
smin(Rx1,x),smin(Ry1,y);
smax(Rx2,x),smax(Ry2,y);
mp[x][y]=1;
W=2*(Rx2-Rx1)+2*(Ry2-Ry1);
}
}
int main()
{
freopen(FROP".in","r",stdin);
freopen(FROP".out","w",stdout);
init();
int ans;
if(Sx==Rx1||Sx==Rx2||Sy==Ry1||Sy==Ry2)
ans=0;
else ans=spfa(Sx,Sy);
printf("%d\n",ans);
return 0;
}
T2:2. Landscaping
题目描述
N(1 <= N <= 100)个数排成一行,值分别为A_i,现在希望把每个数对应地改成B_i。(A_i,B_i的值均在0..10之间)。改变的方式有3种:
(1)把A_i增加到B_i,每增加1,花费
X(2)把Ai减少到Bi,每减少1,花费
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).
这道题有两种解法,,,个人认为特别好,特别是转成CSD做的时候,好厉害。
先说dp吧
首先,先不考虑加于减,只考虑转移,那么你会发现其实一个初始状态确定后,转移的那个值是一定的,而且abs(i-j),这个的i到j与i一步一步到j其实是一样的。
所以可以得到这样的一个方程(C[i]=B[i]-A[i])f[i][j],第i个转移j个所需要的代价,,假装前i-1个已经与B[i]相同
if(C[i]>0)
f[i][j]=min{f[i-1][j-k]+abs(j)*Z+(c[i]-k)*X}(0<=k<=c[i])
else
f[i][j]=min{(f[i-1][j-k]+abs(j)*Z+(k-c[i])}(c[i]<=k<=0)
我详细讲一下这个方程,因为我卡了很久(太蠢了我)
那C[i]>0的情况来讲,处理第i个还需要j转移的代价最小值(准确来说是已经转移了j个的代价值),从第i-1个,需要转移j-k个,然后这个需要往后挪,到了i处,加上i需要转移的一共是j个,所以+abs(j),j也可以是负的,转移可以是收,也可以是给,然后相当于i的纯转移是k个(现在把他想成需要得到k个),但是i实际是需要得到c[i]个,所以需要手动加(c[i]-k)*X;
同理C[i]<=0时,,处理第i个需要给出j个,第i-1个需要给出j+k个,然后往前挪,实际i的纯给出是k个,但是i实际需要给出abs(c[i])个,所以手动给出(c[i]-k)个,这里的k都是整数,但是代码中k是从c[i]-0,所以需要反一下。,好厉害的dp方程,,,
注意下标为负数的情况。。
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,0x3f,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define maxx(x1,x2,x3) max(x1,max(x2,x3))
#define minn(x1,x2,x3) min(x1,min(x2,x3))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "landscaping"
#define C(a,b) next_permutation(a,b)
#define LL long long
#define smin(x,tmp) x=min(x,tmp)
#define smax(x,tmp) x=max(x,tmp)
using namespace std;
const int N = 105,base=1005;
int n,X,Y,Z,V,A[N],B[N],C[N];
inline void init()
{
scanf("%d%d%d%d",&n,&X,&Y,&Z);
for(int i = 1; i <= n; i++)
{
scanf("%d%d",&A[i],&B[i]);
C[i]=B[i]-A[i];
V+=abs(C[i]);
}
}
int dp[N][base*2];
int main()
{
freopen(FROP".in","r",stdin);
freopen(FROP".out","w",stdout);
init();
maxcle(dp);
dp[0][base]=0;
for(int i = 1; i <= n; i++)
if(C[i]>0)
{
for(int j = -V; j <= V; j++)
for(int k = 0 ; k <= C[i]; k++)
smin(dp[i][j+base],dp[i-1][j-k+base]+abs(j)*Z+(C[i]-k)*X);
}
else
{
for(int j = -V; j <= V; j++)
for(int k = C[i]; k<=0 ;k++)/////k为负的时候是<=0
smin(dp[i][j+base],dp[i-1][j-k+base]+abs(j)*Z+(k-C[i])*Y);
}
printf("%d\n",dp[n][base]);
return 0;
}
然后再给出这个方程的刷表法,,方程是相同的,不过大神是先处理的转换,在来处理与原情况的需要加减的情况,,
#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;
}
如果还看不懂这个鬼畜方程就看看大神的原文的详解吧
毕竟不是这个自己写的代码QAQ,被迫
现在来一个更厉害的方法,,,,,,,,,
膜拜标程方法,ORZ
1 2 3 4
4 3 2 0
1 2 2 3 3 3 4 4 4 4
1 1 1 1 2 2 2 3 3
把这两个序列写成这样,,,,,需要一些操作使序列1变成序列2
插入一个值 X
删除一个值 Y
修改值 Z
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,0x3f,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define maxx(x1,x2,x3) max(x1,max(x2,x3))
#define minn(x1,x2,x3) min(x1,min(x2,x3))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "landscaping"
#define C(a,b) next_permutation(a,b)
#define LL long long
#define smin(x,tmp) x=min(x,tmp)
#define smax(x,tmp) x=max(x,tmp)
using namespace std;
const int N = 1005;
int n,X,Y,Z,nx,ny;
int f[N][N];
int A[N],B[N];
int main()
{
freopen(FROP".in","r",stdin);
freopen(FROP".out","w",stdout);
scanf("%d%d%d%d",&n,&X,&Y,&Z);
for(int i = 1; i<= n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
while(x){A[++nx]=i;x--;}
while(y){B[++ny]=i;y--;}
}
maxcle(f);
f[0][0]=0;
for(int i = 1; i <= nx; i++)f[0][i]=i*X;
for(int i = 1; i <= ny; i++)f[i][0]=i*Y;
for(int i = 1; i <= nx; i++)
for(int j = 1; j <= ny; j++)
{
smin(f[i][j],f[i][j-1]+X);
smin(f[i][j],f[i-1][j]+Y);
smin(f[i][j],f[i-1][j-1]+abs(A[i]-B[j])*Z);
}
printf("%d",f[nx][ny]);
return 0;
}
哦,,,这个是标答,表示心很累,,幸好看到了
“editor distance”(CSD)
Solution Notes: We transform each landscape pattern into an array of length at most 1000 by listing out the locations of the individual units of dirt in the landscape in order. For example, if we have a landscape with heights 3,1,4,1, we would transform this into the sequence 0,0,0,1,2,2,2,2,3 (e.g., there are 4 units of dirt at position 2). Our problem now reduces to something very close to the computation of the “edit distance” between two sequences, which is a classical dynamic programming problem. Our goal is to transform one landscape sequence into another at minimum cost given three possible operations: insertion of a new character (at cost X), deletion of a character (at cost Y), or modification of a character (at cost Z times the magnitude of the change). This can be accomplished in O(N^2) time (where N=1000) using dynamic programming, as shown below. Each subproblem C[i][j] we solve along the way represents the minimum cost of transforming just the first i characters of the source sequence into just the first j characters of the target sequence.
T3:
3.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%的数据,表达式中只可能出现两种运算符‘+’和‘-’;
对于其它的数据,四种运算符‘+’,‘-’,‘*’,‘^’在表达式中都可能出现。
对于全部的数据,表达式中都可能出现小括号‘(’和‘)’。
哦,这个我会填上,不想写表达式求值QAQ,,,,,
我一定会写的,,
填坑进行中
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#include<stack>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,127,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define maxx(x1,x2,x3) max(x1,max(x2,x3))
#define minn(x1,x2,x3) min(x1,min(x2,x3))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "equal"
#define C(a,b) next_permutation(a,b)
#define LL long long
#define smin(x,tmp) x=min(x,tmp)
#define smax(x,tmp) x=max(x,tmp)
using namespace std;
//===========================================================================
const int N = 55;
const int mod[15]={0,2683,2687,2689,2693,2699,3331,3527,3529,3533,3571};
const int A[15]={0,1619,1787,1973,2207,2383,2441,2749,2953,2957,2971};
char s[N];
int lens,ans[15];
inline void readstr()
{
cle(s);
lens=0;
char ch=getchar();
while(ch=='\n'||ch==' ')ch=getchar();
while(ch!='\n'&&ch!=EOF)
{
if(ch!=' ')s[++lens]=(char)ch;
ch=getchar();
}//保证一行一行读入
s[0]='(',s[++lens]=')';
}
int priorit(char ch,bool instack)
{
switch (ch)
{
case '+':return instack? 2:1;
case '-':return instack? 2:1;
case '*':return instack? 4:3;
case '^':return instack? 6:5;
case '(':return instack? 0:8;
case ')':return instack? 8:0;//遇到'('都进栈,遇到')'都计算
default :return -1;
}
}
int kmi(int x,int y,int md)
{
if(!y)return 1;
if(y==1)return x;
int p=kmi(x,y/2,md);
if(y%2)return p*p%md*x%md;
return p*p%md;
}
stack<char>opt;
stack<int>num;
inline void cal(int md)
{
char ch=opt.top();opt.pop();
int t2=num.top();num.pop();
int t1=num.top();num.pop();
switch (ch)
{
case '+':num.push((t1+t2)%md);break;
case '-':num.push((t1-t2)%md);break;
case '^':num.push(kmi(t1,t2,md));break;
case '*':num.push((t1*t2)%md);break;
}
}
int exp(int a,int md)
{
while(!opt.empty())opt.pop();
while(!num.empty())num.pop();
int i=0;
while(i<=lens)
{
bool isnum=false;
int res=0;
if(s[i]=='a')isnum=true,res=a,i++;
else
{
while(s[i]>='0'&&s[i]<='9')
res=res*10+s[i]-'0',i++,isnum=true;
}
if(isnum)num.push(res);
if(s[i]=='-'&&s[i-1]=='(')num.push(0);//括号中负数的情况
//bool tmp=opt.empty()?false:true;
//int t1=priorit(opt.top(),true);
//int t2=priorit(s[i],false);
if(i>lens)break;//注意判断符号栈和s[i]是否还有!!!
while(!opt.empty()&&priorit(opt.top(),true)>priorit(s[i],false))
cal(md);
if(s[i]==')')opt.pop();//左括号出栈
else opt.push(s[i]);
i++;//!
}
return (num.top()%md+md)%md;//直接不算负数
}
int n;
int main()
{
freopen(FROP".in","r",stdin);
freopen(FROP".out","w",stdout);
readstr();
for(int i = 1; i <= 10; i++)
ans[i]=exp(A[i],mod[i]);
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
readstr();
bool istrue=true;
for(int j = 1; j <= 10; j++)
{
int tmp=exp(A[j],mod[j]);
if(tmp!=ans[j])
{
istrue=false;
break;
}
}
if(istrue)printf("%c",i+'A'-1);
}
return 0;
}