记 2024.4.18
AT_agc008_d
[AGC008D] K-th K
题面翻译
给你一个长度为 的整数序列 ,请判断是否存在一个满足下列条件的整数序列 ,如果存在,请构造一种方案。
条件如下:
-
的长度为 ,并且满足数字 都各出现恰好 次。
-
对于 ,数字 在 中第 次出现的位置是 。
题目描述
長さ の数列 が与えられます。 次の条件をすべて満たす数列 が存在するか判定し、存在するならば を つ構成してください。
- は長さ であり、整数 , , , をそれぞれちょうど 個ずつ含む。
- 各 について、 に含まれる整数 のうち左から 番目に位置するものは、 全体では左から 番目に位置する。
输入格式
入力は以下の形式で標準入力から与えられる。
输出格式
条件をすべて満たす数列 が存在しないならば、No を出力せよ。 存在するならば、 行目に Yes を出力し、 行目に を空白区切りで出力せよ。
样例 #1
样例输入 #1
3
1 5 9
样例输出 #1
Yes
1 1 1 2 2 2 3 3 3
样例 #2
样例输入 #2
2
4 1
样例输出 #2
No
提示
制約
- はすべて相異なる。
Sample Explanation 1
たとえば、 に含まれる整数 のうち左から 番目に位置するものは、 全体では左から 番目に位置しています。 整数 , についても同様に条件が成り立っています。
可以想到贪心策略,但少考虑了某些情况,导致不看题解被卡2h。
注意到如果先处理离目前处理到的位置最近的要求一定是最优的,所以 放离 位置最近的未完成要求的编号。(编号指输入的顺序,因为不保证一定递增,所以要排序)
漏考虑不能只正着填,还要倒着填一次,因为如果需求填完了之后就开始填从位置小到大的总共须填的值,这样有可能在某个结束需求位置之前,导致之前的个数变多了。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int N=250010;
int a[N],sum[N],res[N];
pair<int,int> x[N];
int n;
int st[N];
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>x[i].first,x[i].second=i;
sort(x+1,x+1+n);
for(int i=1;i<=n;i++)
{
a[x[i].first]=x[i].second;
st[x[i].first]=i;
sum[i]=x[i].second-1;
res[i]=n-1;
}
bool flag=1;
for(int i=1;i<=n*n;i++)
{
if(st[i])
{
for(int j=1;j<=st[i];j++)
if(sum[j]>0)
{
puts("No");
return 0;
}
continue;
}
bool flagg=1;
if(flag) for(int j=1;j<=n;j++)
if(sum[j]>0)
{
flagg=0;
sum[j]--;
res[j]--;
a[i]=x[j].second;
break;
}
//for(int j=1;j<=n;j++)
//{
// cout<<i<<' '<<j<<":"<<res[x[j].second]<<' '<<sum[x[j].second]<<endl;
//}
}
for(int i=1;i<=n;i++)
{
a[x[i].first]=x[i].second;
st[x[i].first]=i;
sum[i]=n-x[i].second;
res[i]=n-1;
}
flag=1;
for(int i=n*n;i>=1;i--)
{
if(st[i])
{
for(int j=n;j>=st[i];j--)
if(sum[j]>0)
{
puts("No");
return 0;
}
continue;
}
bool flagg=1;
if(flag) for(int j=n;j>=1;j--)
if(sum[j]>0)
{
flagg=0;
sum[j]--;
res[j]--;
a[i]=x[j].second;
break;
}
//for(int j=1;j<=n;j++)
//{
// cout<<i<<' '<<j<<":"<<res[x[j].second]<<' '<<sum[x[j].second]<<endl;
//}
if(!a[i])
{
puts("No");
return 0;
}
}
puts("Yes");
for(int i=1;i<=n*n;i++) cout<<a[i]<<' ';
return 0;
}
注意以后如果可以用目前已做的做法经少量修改得到需要实现的一个操作,那么一定要用。
P1433
吃奶酪
题目描述
房间里放着 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 点处。
输入格式
第一行有一个整数,表示奶酪的数量 。
第 到第 行,每行两个实数,第 行的实数分别表示第 块奶酪的横纵坐标 。
输出格式
输出一行一个实数,表示要跑的最少距离,保留 位小数。
样例 #1
样例输入 #1
4
1 1
1 -1
-1 1
-1 -1
样例输出 #1
7.41
提示
数据规模与约定
对于全部的测试点,保证 ,,小数点后最多有 位数字。
提示
对于两个点 ,,两点之间的距离公式为 。
:新增加一组 数据。
考虑状态 表示目前的吃过奶酪数是 ,现在在 的位置所需的距离。
性质 MIN 。
状态计算,,。
注意先想好状态以及状态转移在写代码,先别急。
Code
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const int N=18;
double f[1<<18][17];
double x[N],y[N];
int n;
double to(int a,int b)
{
return sqrt(pow(fabs(x[a]-x[b]),2)+pow(fabs(y[a]-y[b]),2));
}
int main()
{
cin>>n;
for(int i=0;i<(1<<(n+1));i++) for(int j=0;j<=n;j++) f[i][j]=1e9;
for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
x[0]=0,y[0]=0;
f[0][0]=0;
for(int i=0;i<(1<<(n+1));i++)
{
for(int j=0;j<=n;j++)
{
for(int k=0;k<=n;k++) if(i>>j&1) f[i][j]=min(f[i][j],f[i-(1<<j)][k]+to(k,j));
}
}
double minn=1e9;
for(int i=0;i<=n;i++)
{
minn=min(minn,f[(1<<(n+1))-1][i]);
}
printf("%.2lf",minn);
return 0;
}

浙公网安备 33010602011771号