1. 程式人生 > >TopCoder SRM 568 Div1 500 EqualSums

TopCoder SRM 568 Div1 500 EqualSums

這題可以說是花了一個多月才解決(霧 暑假的時候就很認真想過了,就是差了一步,昨天突然開竅。 emmm我真棒

我們發現題目的條件可以轉化為對於任意a[i][j]=a[i][1]+a[1][j]a[1][1](i>1,j>1)a[i][j]=a[i][1]+a[1][j]-a[1][1](i>1,j>1) 有些a[i][j]a[i][j]是已知的,那麼我們就可以得到一些條件形如a[i][1]+a[1][j]=a[i][j]+a[1][1

]a[i][1]+a[1][j]=a[i][j]+a[1][1] 但這樣還不夠,由於每個數都要>=0>=0,也就是說對任意i,ji,ja[i][1]+a[1][j]>=a[1][1]a[i][1]+a[1][j]>=a[1][1] 就相當於要min{a[i][1]}+min{a[1][j]}>=a[1][1]min\{a[i][1]\}+min\{a[1][j]\}>=a[1][1]

於是我們就可以把所有a[i][1]a[i][1]a[1][j]a[1][j]單獨拿出來,設為c1c_1c2n2c_{2n-2},若ci+cj=vc_i+c_j=v就在i,ji,j之間連一條權值為vv的邊。對於一個聯通塊,只要其中一個點的權值確定了,整個聯通塊的點就都確定了。於是,我們就暴枚聯通塊中的一個點,把整個聯通塊求出來。再用f[i][j][k]f[i][j][k],表示前ii個聯通塊,在已經求出的點中,min{a[

w][1]}=jmin\{a[w][1]\}=jmin{a[1][w]}=kmin\{a[1][w]\}=k的方案數。滾動陣列優化掉一維,最後統計時只要保證j+k>=a[1][1]j+k>=a[1][1]就行了。

如果a[1][1]a[1][1]不知道的話,有兩種辦法。第一種是暴力列舉。第二種的話,我們可以發現交換任意兩行或任意兩列答案不變,就把一個知道的交換到a[1][1]a[1][1]就行了。

#include <bits/stdc++.h>
#define fr(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
const int N=51;
const int M=N<<1;
const int p=1e9+7;
int n,a[N][N],x,m;
int g[M][M];
int c[M];
int b[M],vis[M];
int f[2][20][20];

template<class T> void checkmin(T &a,const T &b) { if (b<a) a=b; } 
template<class T> void checkmax(T &a,const T &b) { if (b>a) a=b; }

class EqualSums {
public:
    int count( vector <string> board ) ;
};

void Add(int &x,int y){
	x+=y;
	while(x>=p) x-=p;
}

void init(){
	memset(c,-1,sizeof c);
	fr(i,2,n) if (a[i][1]!=-1) c[i-1]=a[i][1];
    fr(i,2,n) if (a[1][i]!=-1) c[i+n-2]=a[1][i];
}

void visit(int x){
	b[x]=1;
	for(int i=1;i<=m;i++)
	 if (g[x][i]!=-1&&!b[i]) visit(i);
}

int dfs(int x){
	vis[x]=1;
	if (c[x]<0) return 0;
	for(int i=1;i<=m;i++){
		if (g[x][i]==-1) continue;
		if (vis[i]&&c[x]+c[i]!=g[x][i]) return 0;
		if (vis[i]) continue;
		if (c[i]!=-1&&c[x]+c[i]!=g[x][i]) return 0;
		c[i]=g[x][i]-c[x];
		if (!dfs(i)) return 0;
	}
	return 1;
}

int EqualSums::count(vector <string> board) {
    n=board.size();
    m=2*n-2;
    for(int i=0;i<n;i++)
     for(int j=0;j<n;j++)
     	if (board[i][j]=='-') a[i+1][j+1]=-1;
    	 else a[i+1][j+1]=board[i][j]-'0';
    int q=1,w=1;
    fr(i,1,n) fr(j,1,n) if (a[i][j]!=-1){ q=i;w=j;break; }
    //fr(i,1,n) swap(a[1][i],a[q][i]);
    //fr(i,1,n) swap(a[i][1],a[i][w]);
    int z,y;
    if (a[1][1]==-1) z=0,y=18;
     else z=a[1][1],y=a[1][1];
    int ans=0;
    for(x=z;x<=y;x++){
    	memset(g,-1,sizeof g);
    	memset(c,-1,sizeof c);
    	memset(f,0,sizeof f);
    	memset(b,0,sizeof b);
    	fr(i,2,n)
    	 fr(j,2,n)
    	  if (a[i][j]!=-1) g[i-1][j+n-2]=g[j+n-2][i-1]=a[i][j]+x;
    	//cout<<x<<endl;
    	//cout<<g[1][2]<<' '<<g[2][1];
    	init();
    	int cur=0;
    	f[0][19][19]=1;
    	//cout<<c[1]<<endl;
    	for(int i=1;i<=m;i++)
    	 if (!b[i]){
    	 	
    	 	init();
    	 	//cout<<c[1]<<endl;
    	 	//printf("i=%d,c[i]=%d\n",i,c[i]);
    	 	int zz,yy;
    	 	if (c[i]!=-1) zz=yy=c[i];
    	 	 else zz=0,yy=18;
    	 	cur^=1;
    	 	memset(f[cur],0,sizeof f[cur]);
    	 	visit(i);
    	 	fr(j,zz,yy){
    	 	 	init();
    	 	 	c[i]=j;
    	 	 	memset(vis,0,sizeof vis);
    	 	 	if (!dfs(i)) continue;
    	 	 	//cout<<j<<endl;
    	 	 	int mn1=19,mn2=19;
    	 	 	for(int k=1;k<n;k++) if (c[k]>=0) mn1=min(mn1,c[k]);
    	 	 	for(int k=n;k<=m;k++) if (c[k]>=0) mn2=min(mn2,c[k]);
    	 	 	//printf("%d %d %d\n",j,mn1,mn2);
    	 	 	for(int k=0;k<=19;k++)
    	 	 	 for(int l=0;l<=19;l++)
    	 	 	  if (f[cur^1][k][l]) Add(f[cur][min(mn1,k)][min(mn2,l)],f[cur^1][k][l]);
    	 	}
    	 }
    	fr(i,0,18)
    	 fr(j,0,18)
    	  if (i+j>=x) Add(ans,f[cur][i][j]);
    }
    return ans;
}