题意:
给一个n,m的矩阵dx,y,构造一张小于300个点的有向图,边上的权值范围为[0,100],也可以是未知整数x或y,要求给出固定的S,T,当x分别取[1,n],y分别取[1,m]时,S到T的最短路为 dx,y
1≤n,m≤10
1≤dx,y≤100(1≤x≤n,1≤y≤m)
Solution:
正解为贪心建图然后重新计算d矩阵看和原来矩阵是否相同,复杂度 O(n3)
自己YY了一个做法:
S到T每条路径所对应的值为 ax+by+c ,这个值对应d数组,一定满足 ax+by+c≥dx,y ,那么我们可以枚举a,b,c,枚举x,y,遇到符合条件的a,b,c就把他存入一个集合,只到集合中所有的三元组都覆盖了 dx,y ,我们就开始建图,可以证明如果可以建图,那么最终的点数一定小于300,注意重边问题,具体细节看代码。最终复杂度 O(n4) ,也是可以过的。
代码:
#include<cstdio>
#include<iostream>
using namespace std;
int d[11][11],n,m,tot,now,sum;
int pa[110],pb[110];
bool vis[11][11];
struct edg{
int a,b,c;
}ans[310];
void print()
{
int numn=0,numm=0;
for (int i=1;i<=sum;i++)
{
numm+=ans[i].a+ans[i].b+1;
numn+=ans[i].a+ans[i].b;
}
numn+=2;
if (numn>300) {
while(1);printf("Impossible\n");return;}
printf("Possible\n");
printf("%d %d\n",numn,numm);
int t=1,pre=1;
for (int i=1;i<=sum;i++)
{
pre=1;
printf("%d %d %d\n",pre,++t,ans[i].c),pre=t;
for (int j=1;j<=ans[i].a;j++)
{
printf("%d %d %c\n",pre,(j==ans[i].a&&ans[i].b==0)?numn:++t,'X'),pre=t;
}
for (int j=1;j<=ans[i].b;j++)
printf("%d %d %c\n",pre,(j==ans[i].b)?numn:++t,'Y'),pre=t;
}
printf("%d %d\n",1,numn);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&d[i][j]);
for (int s=0;s<=300;s++)
for (int a=0;a<=s&&a<=100;a++)
for (int b=0;b<=s-a&&b<=100;b++)
{
int c=s-a-b;
bool p=0,q=0;
int now=0;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (a*i+b*j+c==d[i][j]&&!vis[i][j]) q=1,now++,pa[now]=i,pb[now]=j;
else if (a*i+b*j+c<d[i][j]) {p=1;break;}
}
if (p==1) break;
}
if ((!p)&&q)
{
for (int i=1;i<=now;i++)
vis[pa[i]][pb[i]]=1;
tot+=now,ans[++sum].a=a,ans[sum].b=b,ans[sum].c=c;
if (tot==n*m){
print();return 0;}
}
}
printf("Impossible\n");
}