很容易想到按ai排序。
然后ai>i就不行。
然后就往前面连,保证恰好能到达ai个点。
看了官方题解就是往前a[i]个点都连边就好了。
由于有向无环,所以不可能到达比a[i]大的点,而小于等于a[i]的点我们又全都到达了。
我只想到了一个比较复杂的方法,算法错了,又改才对了。
就是讨论,如果a[i]=a[i-1]+1,那就直接连到i-1
如果a[i]=a[i-1],那就跟i-1一样连。
否则一定存在足够多的入度为0的点,我们不连他们,但连其他所有的点。
一开始有些地方想错了,就是想得不够清楚,所以WA了挺久。
算法实现有些麻烦。
希望自己以后能想明白,一方面降低出错的可能,另一方面降低代码复杂度。
感觉自己贪心的题目总是讨论得很复杂,主要原因就是没想彻底,然后就着急着写了。
代码
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
const int maxn = 1010;
vector<pii>vec;
int N;
int a[maxn];
int r[maxn];
int v[maxn];
int vis[maxn];
int viss[maxn];
vector<int>G[maxn];
int cmp(int i,int j)
{
return a[i]<a[j];
}
void read()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d",a+i);
++a[i];
r[i]=i;
v[i]=0;
G[i].clear();
}
}
void add(int u,int v)
{
if(v)
{
G[u].push_back(v);
vec.push_back(make_pair(r[u],r[v]));
}
}
bool solve()
{
vec.clear();
read();
sort(r+1,r+1+N,cmp);
for(int i=1;i<=N;i++)
{
if(a[r[i]]>i) return false;
if(a[r[i]]==a[r[i-1]])
for(int j=0;j<(int)G[i-1].size();j++)
add(i,G[i-1][j]);
else if(a[r[i]]==a[r[i-1]]+1)
{
add(i,i-1);
v[i-1]=0;
}
else
{
int num = i-a[r[i]];
for(int j=1;j<i;j++) vis[j]=viss[j]=0;
for(int j=1;j<i;j++)
{
if(!v[j]) viss[j]=1;
else if(num)
{
--num;
if(vis[a[j]]) continue;
vis[a[j]]=1;
for(int k=0;k<(int)G[j].size();k++)
viss[G[j][k]]=1;
}
else viss[j]=1;
}
for(int j=1;j<i;j++) if(viss[j]) add(i,j),v[j]=0;
}
v[i]=1;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
printf("Case #%d: ",t);
if(solve())
{
puts("Yes");
printf("%d\n",(int)vec.size());
for(int i=0;i<(int)vec.size();i++)
printf("%d %d\n",vec[i].first,vec[i].second);
}
else puts("No");
}
return 0;
}