链接
1.https://www.cnblogs.com/wutongtong3117/p/7673538.html
2.https://blog.csdn.net/DancingZ/article/details/82707974
3.https://www.cnblogs.com/wutongtong3117/p/7673535.html
4.https://www.cnblogs.com/yangqingli/p/4927698.html
T1 足球联赛(soccer.pas/c/cpp)
题目
传送门:mzoj1495
【问题描述】
巴蜀中学新一季的足球联赛开幕了。足球联赛有 n 只球队参赛,每赛季,每只球队要与其他球队各赛两场,主客各一场,赢一场得 3 分,输一场不得分,平局两只队伍各得一分。
英勇无畏的小鸿是机房的主力前锋,她总能在关键时刻踢出一些匪夷所思的妙球。但是很可惜,她过早的燃烧完了她的职业生涯,不过作为一个能够 Burning 的 girl,她的能力不止如此,她还能预测这个赛季所有球队的比赛结果。虽然她能准确预测所有比赛的结果,但是其实她不怎么厉害,Mr.Gao 上数学课时她总是在 sleep,因此她的脑里只有整数没有实数,而且,她只会 10 以内非负整数的加法运算,因此她只有结果却无法知道谁会获得联赛的冠军。
小鸿想给冠军队伍的所有队员一个拥抱,所以她把计算结果的任务交给了你:现在,给你一个 n*n 的矩阵表示比赛情况。第 i 行第 j 列的字母表示在第 i 只队伍在主场迎战第 j 只队伍的比赛情况,W 表示主队赢,L 表示主队输,D 表示平局。现在需要你给出最后能得到小鸿拥抱的队伍编号,如有多支队伍分数最高,按字典序输出编号。
【输入格式】
第一行一个整数 n。
接下来 n 行,每行 n 个字符,表示输赢情况。
第 i 行第 i 列为 - ,因为一只队伍不可能与自己比赛。
【输出格式】
输出得分最高的队伍编号。如有多个在一行中输出,用一个空格分开。
【样例输入输出 1】
soccer.in
3
-WW
W-W
WW-
soccer.out
1 2 3
【样例输入输出 2】
soccer.in
5
-DWWD
L-WLL
DD-WD
DDL-L
DDLL-
soccer.out
1
【数据范围】
对于 40%的数据,满足 N<=20
对于 100%的数据,满足 N<=50
代码
/*******************************
User:Mandy.H.Y
Language:c++
Problem:soccer
Algorithm:
Date:2019.7.15
Scores:
*******************************/
//道阻且长,行则必至
#include<bits/stdc++.h>
using namespace std;
const int maxn=55;
int n,ans[maxn],mans=0;
char c[maxn][maxn];
template<class T>inline void read(T &x){
x=0;bool flag=0;char ch=getchar();
while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(flag)x=-x;
}
template<class T>void putch(const T x){
if(x>9) putch(x/10);
putchar(x%10|48);
}
template<class T>void put(const T x){
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("soccer.in","r",stdin);
freopen("soccer.out","w",stdout);
}
void readdata(){
read(n);
for(int i=0;i<n;++i)
scanf("%s",c[i]);
}
void work(){
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
if(i==j) continue;
switch(c[i][j]){
case 'W':{
ans[i]+=3;
break;
}
case 'L':{
ans[j]+=3;
break;
}
default:{
ans[i]++;
ans[j]++;
break;
}
}
mans=max(mans,max(ans[i],ans[j]));
}
}
for(int i=0;i<n;++i){
if(ans[i]==mans){
put(i+1);putchar(' ');
}
}
}
int main(){
file();
readdata();
work();
return 0;
}
T2 最短路径(paths.pas/c/cpp)
题目
传送门:mzoj1496
【问题描述】
平面内给出 n 个点,记横坐标最小的点为 A,最大的点为 B,现在小 Y 想要知道在每个点经过一次(A 点两次)的情况下从 A 走到 B,再回到 A 的最短路径。但他是个强迫症患者,他有许多奇奇怪怪的要求与限制条件:
1.从 A 走到 B 时,只能由横坐标小的点走到大的点。
2.由 B 回到 A 时,只能由横坐标大的点走到小的点。
3.有两个特殊点 b1 和 b2, b1 在 0 到 n-1 的路上,b2 在 n-1 到 0 的路上。
请你帮他解决这个问题助他治疗吧!
【输入格式】
第一行三个整数 n,b1,b2,( 0 < b1,b2 < n-1 且 b1 <> b2)。n 表示点数,从 0 到 n-1 编号,b1 和 b2 为两个特殊点的编号。
以下 n 行,每行两个整数 x、y 表示该点的坐标(0 <= x,y <= 2000),从 0 号点顺序给出。
Doctor Gao 为了方便他的治疗,已经将给出的点按 x 增序排好了。
【输出格式】
输出仅一行,即最短路径长度(精确到小数点后面 2 位)
【样例输入输出】
paths.in
5 1 3
1 3
3 4
4 1
7 5
8 3
paths.out
18.18
【样例解释】
最短路径:0->1->4->3->2->0
【数据范围】
20%的数据 n<=20
60%的数据 n<=300
100%的数据 n<=1000
对于所有数据 x,y,b1,b2如题目描述
代码
/*******************************
User:Mandy.H.Y
Language:c++
Problem:paths
Algorithm:DP
Date:2019.7.15
Scores:
*******************************/
//尽小者大,慎微者著
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n,b1,b2;
double dp[maxn][maxn],dis[maxn][maxn];
//与传纸条有点像
//看成两个人走两个不同路线
//dp[第一个人的位置][第二个人的位置]=最短路径
//保证走到 max(i,j)时,前面的点都走到
//满足最优子问题 -> DP
struct Node{
int x,y;
}node[maxn];
template<class T>inline void read(T &x){
x=0;bool flag=0;char ch=getchar();
while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(flag)x=-x;
}
template<class T>void putch(const T x){
if(x>9) putch(x/10);
putchar(x%10|48);
}
template<class T>void put(const T x){
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("paths.in","r",stdin);
freopen("paths.out","w",stdout);
}
void readdata(){
read(n);read(b1);read(b2);++b1,++b2;
for(int i=1;i<=n;++i){
read(node[i].x);read(node[i].y);
}
}
void init(){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
dp[i][j]=dp[j][i]=2e9;
if(i==j) continue;
dis[i][j]=sqrt(((double)node[i].x-node[j].x)*((double)node[i].x-node[j].x)
+((double)node[i].y-node[j].y)*((double)node[i].y-node[j].y));
dis[j][i]=dis[i][j];
}
}
}
void work(){
init();
dp[1][1]=0;
dp[1][2]=dis[1][2];
dp[2][1]=dis[1][2];
for(int i=1;i<=n;++i){
if(i==b2) continue;
for(int j=1;j<=n;++j){
if(i==j) continue;
if(j==b1) continue;//有b1与b2的存在,所以i,j不对称
if(j<i-1 && i-1!=b2) dp[i][j]=min(dp[i][j],dp[i-1][j]+dis[i-1][i]);
//由于前面的点必须走到,而j<i-1,所以上一步 i 必须走到 i-1
if(j==i-1) {
for(int k=1;k<j;++k){
if(k!=b2)//判断是否合法
dp[i][j]=min(dp[i][j],dp[k][j]+dis[i][k]);
}
}
if(i<j-1 && j-1!=b1) dp[i][j]=min(dp[i][j],dp[i][j-1]+dis[j][j-1]);
if(j-1==i) {
for(int k=1;k<i;++k){
if(k!=b1)
dp[i][j]=min(dp[i][j],dp[i][k]+dis[j][k]);
}
}
}
}
for(int i=1;i<=n-1;++i){
dp[n][n]=min(dp[n][n],dp[n][i]+dis[i][n]);
dp[n][n]=min(dp[n][n],dp[i][n]+dis[i][n]);//两个都要考虑,因为不对称;
}
printf("%.2lf",dp[n][n]);
}
int main(){
file();
readdata();
work();
return 0;
}
/*******************************
User:Mandy.H.Y
Language:c++
Problem:paths
Algorithm:DP
Date:2019.7.15
Scores:
*******************************/
/*
题解:https://www.cnblogs.com/wutongtong3117/p/7673537.html
用f[i][j]表示第一个点走到i,第二个点(回去的那个点)走到j的最优值。
为了保证更新时不会更新出f[i][i],而且每个点都会在路径上,
我们每次用f[i][j]去更新点max(i,j)+1,所以转移方程为:
f[1][1]=0;
k=max(i,j)+1,
f[k][j]=min(f[k][j],f[i][j]+dis(i,k));
f[i][k]=min(f[i][k],f[i][j]+dis(j,k));
因为i,j使从1到n循环的,所以每个点都可以分配到来路或去路
对于两个特殊点
if (j==n) f[n][n]=min(f[n][n],f[i][n]+dis(i,n));
if (i==n) f[n][n]=min(f[n][n],f[n][j]+dis(j,n));
//一个节点到头了,只能动另一个节点
if (k!=b1) f[i][k]=min(f[i][k],f[i][j]+dis(j,k));
if (k!=b2) f[k][j]=min(f[k][j],f[i][j]+dis(i,k));
*/
//尽小者大,慎微者著
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n,b1,b2;
double dp[maxn][maxn],dis[maxn][maxn];
//与传纸条有点像
//看成两个人走两个不同路线
//dp[第一个人的位置][第二个人的位置]=最短路径
//保证走到 max(i,j)时,前面的点都走到
//满足最优子问题 -> DP
struct Node{
int x,y;
}node[maxn];
template<class T>inline void read(T &x){
x=0;bool flag=0;char ch=getchar();
while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(flag)x=-x;
}
template<class T>void putch(const T x){
if(x>9) putch(x/10);
putchar(x%10|48);
}
template<class T>void put(const T x){
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("paths.in","r",stdin);
freopen("paths.out","w",stdout);
}
void readdata(){
read(n);read(b1);read(b2);++b1,++b2;
for(int i=1;i<=n;++i){
read(node[i].x);read(node[i].y);
}
}
void init(){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
dp[i][j]=dp[j][i]=2e9;
if(i==j) continue;
dis[i][j]=sqrt(((double)node[i].x-node[j].x)*((double)node[i].x-node[j].x)
+((double)node[i].y-node[j].y)*((double)node[i].y-node[j].y));
dis[j][i]=dis[i][j];
}
}
}
void work(){
init();
dp[1][1]=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if((i!=1||j!=1)&&i==j) continue;
int k=max(i,j)+1;
if(k==n+1){
if(i==n) dp[n][n]=min(dp[n][n],dp[i][j]+dis[j][n]);
if(j==n) dp[n][n]=min(dp[n][n],dp[i][j]+dis[i][n]);
} else{
if(k!=b2) dp[k][j]=min(dp[k][j],dp[i][j]+dis[i][k]);//回的时候不走b1
if(k!=b1) dp[i][k]=min(dp[i][k],dp[i][j]+dis[k][j]);//去的时候不走b2
}
}
}
printf("%.2lf",dp[n][n]);
}
int main(){
// file();
readdata();
work();
return 0;
}
T3 阿 Q 的停车场(park.pas/c/cpp)
传送门:mzoj1497
问题描述
刚拿到驾照的 KJ 总喜欢开着车到处兜风,玩完了再把车停到阿 Q 的停车场里,虽然 她对自己停车的水平很有信心,但她还是不放心其他人的停车水平,尤其是 Kelukin。于是, 她每次都把自己的爱车停在距离其它车最远的一个车位。
KJ 觉得自己这样的策略非常科 学,于是她开始想:在一个停车场中有一排车位,从左到右编号为 1 到n,初始时全部是 空的。有若干汽车,进出停车场共 m 次。
对于每辆进入停车场的汽车,会选择与其它车距 离最小值最大的一个车位,若有多个符合条件,选择最左边一个。KJ 想着想着就睡着了, 在她一旁的 Kelukin 想帮她完成这个心愿,但是他又非常的懒,不愿意自己动手,于是就把 这个问题就留给了你:在 KJ 理想的阿 Q 的停车场中,给你车辆进出的操作序列,依次输 出每辆车的车位编号。
输入格式
第一行,两个整数 n 和 m,表示停车场大小和操作数;
接下来 m 行,每行两个整数 F 和 x F 是 1 表示编号为 x 的车进停车场; F 是 2 表示编号为 x 的车出停车场;
保证操作合法,即: 出停车场的车一定目前仍在停车场里; 停车场内的车不会超过 n;
输出格式
对于所有操作 1,输出一个整数,表示该车车位的编号
样例输入
7 11
1 15
1 123123
1 3
1 5
2 123123
2 15
1 21
2 3
1 6
1 7
1 8
样例输出
1
7
4
2
7
4
1
3
提示
【数据范围】
对 30%的数据 n<=1000 ,m<=1000 对
60%的数据 n<=200000,m<=2000
对 100%的数据 n,m<=200000,
车的编号小于等于 10^6
代码
/*******************************
User:Mandy.H.Y
Language:c++
Problem:park
Algorithm:
Date:2019.7.15
Scores:
*******************************/
//若网在纲,有条不紊
//solution:https://www.cnblogs.com/wutongtong3117/p/7673535.html 不过这篇题解的代码有点问题
//一般情况下,只要找出该区间内没有停车的最远的区间,
//区间长度>>1就是下一辆要停的车与其他车相距的最远距离
//要维护四个量
//分别是l,r,mid,pos
//l表示在当前结点线段树所在区间,最左边的车停的位置
//同理,r表示做右边的车所停的位置
//mid表示在这个小区间[x,y]中的紧邻的两辆车的最长距离除以2后的值
//pos表示取得mid值是所在的紧邻的两辆车的中间位置,也就是在[x,y]中的答案值
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,m,mid,mdis=2e9;
int car[1000005];
struct Node{
int l,r,mid,pos;
}tree[maxn<<2];
template<class T>inline void read(T &x){
x=0;bool flag=0;char ch=getchar();
while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(flag)x=-x;
}
template<class T>void putch(const T x){
if(x>9) putch(x/10);
putchar(x%10|48);
}
template<class T>void put(const T x){
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("park.in","r",stdin);
// freopen("park.out","w",stdout);
}
void readdata(){
read(n);read(m);
}
void update(int k){
if(!k) return;
int lson=k<<1;
int rson=k<<1|1;
if(tree[lson].l) tree[k].l=tree[lson].l;
else tree[k].l=tree[rson].l;
if(tree[rson].r) tree[k].r=tree[rson].r;
else tree[k].r=tree[lson].r;//如果等于零,说明这个区间是空的,就以另一个区间为准
tree[k].mid=tree[lson].mid;tree[k].pos=tree[lson].pos;
if(tree[lson].r&&tree[rson].l){//这里不能等于零,因为必须要有边界
int len=(tree[rson].l-tree[lson].r)>>1;
//由于与之相比较的是 tree[1].x-1,n-tree[1].y所以直接 tree[rc].x-tree[lc].y
//由于是两个区间之间的空位,故 tree[rc].x-tree[lc].y
if(len>tree[k].mid){
tree[k].mid=len;
tree[k].pos=(tree[rson].l+tree[lson].r)>>1;
}
}
if(tree[rson].mid>tree[k].mid){
tree[k].mid=tree[rson].mid;
tree[k].pos=tree[rson].pos;
}//左中右,保证字典序最小
return;
}
void add(int l,int r,int k,int pos,int val){
if(l==r&&l==pos){
if(val){
tree[k].l=pos;tree[k].r=pos;
tree[k].mid=0;tree[k].pos=0; //节点上有车了,当然就没有mid和p值了
}else{
tree[k].l=0;tree[k].r=0;
tree[k].mid=0;tree[k].pos=0;
}
return;
}
int mid=(l+r)>>1;
if(pos<=mid) add(l,mid,k<<1,pos,val);
if(pos>mid) add(mid+1,r,k<<1|1,pos,val);
update(k);
}
void work(){
mid=1;
mdis=0;
while(m--){
int opt,u;
read(opt);read(u);
if(opt==1){
if(!tree[1].l){
puts("1");
car[u]=1;
}else{
int len=-1;
if(tree[1].l-1>len){
len=tree[1].l-1;
car[u]=1;
}//第一个车位
if(tree[1].mid>len){
len=tree[1].mid;
car[u]=tree[1].pos;
}
if(n-tree[1].r>len){
len=n-tree[1].r;
car[u]=n;
}//最后一个车位
put(car[u]);putchar('\n');
}
add(1,n,1,car[u],1);//加车
}else add(1,n,1,car[u],0);
}
}
int main(){
// file();
readdata();
work();
return 0;
}