【USACO2.3】解题报告

前言

本章主要是动态规划,但是其中几道题的方法比较多,我采用了其他的方法。
题目已经开始变难,有几道题还特别值得思考。已经达到普及 T 3 / T 4 T3/T4 T3/T4水平。
USACO:http://train.usaco.org/


2.3.1.Longest Prefix

博 客 地 址 博客地址


2.3.2.Cow Pedigrees

思路:

这道题还是很有难度的。
很明显是一个 D P DP DP,但是一开始一直在往(要递归的那种)树形 D P DP DP那方面想,后来发现不行,于是就开始推方程。
考虑到每种转移都是和第 ≤ i − 1 \leq i-1 i1层有关的,所以就想到设 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个节点在前 j j j层的方案数。
那么很明显,如果根节点左边有 k k k个节点,那么右边就有 i − k − 1 i-k-1 ik1个节点,那么左右的方案数就相乘即可。
那么方程就是
f [ i ] [ j ] = ∑ k = 1 k ≤ i & & ( k & 1 ) f [ i ] [ j ] + f [ k ] [ j − 1 ] × f [ i − k − 1 ] [ j − 1 ] f[i][j]=\sum^{k\leq i\&\&(k\&1)}_{k=1}f[i][j]+f[k][j-1]\times f[i-k-1][j-1] f[i][j]=k=1ki&&(k&1)f[i][j]+f[k][j1]×f[ik1][j1]
最终答案即为 f [ n ] [ m ] − f [ n ] [ m − 1 ] f[n][m]-f[n][m-1] f[n][m]f[n][m1]


代码:

/*
ID:ssl_zyc2
TASK:nocows
LANG:C++
*/
#include 
using namespace std;

const int N=300;
const int MOD=9901;
int n,m,f[N][N];

int main()
{
	freopen("nocows.in","r",stdin);
	freopen("nocows.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
		f[1][i]=1;
	for (int i=2;i<=n;i++)
		for (int j=1;j<=m;j++)
			for (int k=1;k<i;k++)
				f[i][j]=(f[i][j]+f[k][j-1]*f[i-k-1][j-1])%MOD;
	printf("%d\n",((f[n][m]-f[n][m-1])%MOD+MOD)%MOD);
	return 0;
}

2.3.3.Zero Sum

思路:

基础深搜题。每一个位置搜索放置空格, + + + − - ,然后 c h e c k check check一下就可以了。

代码:

#include 
using namespace std;

int a[10],n;

bool check()
{
    int s=0,k=0,p=1;  //分别表示总和,这一部分的值和符号
    for (int i=1;i<=n;i++)
        if (a[i])  //不是空格
        {
            if (p==1) s+=k;
            if (p==2) s-=k;
            p=a[i];  //记录符号
            k=i;
        }
        else k=k*10+i;  //加空格
    if (p==1) s+=k;
    if (p==2) s-=k;
    if (!s) return 1;  //符合
    return 0;
}

void print()
{
    putchar('1');
    for (int i=2;i<=n;i++)
    {
        if (a[i]==0) putchar(' ');
        if (a[i]==1) putchar('+');
        if (a[i]==2) putchar('-');
        putchar(i+48);
    }
    printf("\n");
}

void dfs(int x)
{
    if (x>n)
    {
        if (check()) print();
        return;
    }
    for (int i=0;i<=2;i++)  //一定要按照空格,+再到-的顺序枚举,因为要求字典序
    {
        a[x]=i;
        dfs(x+1);
    }
}

int main()
{
    scanf("%d",&n);
    a[1]=1;  //第一位必须是+
    dfs(2);
    return 0;
}

2.3.4.Money Systems

完全背包模板。
QWQ

代码:

#include 
#define ll long long
#define N 10100
#define M 30
using namespace std;

ll f[N];
int n,m,a[M];

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
     scanf("%d",&a[i]);
    f[0]=1;
    for (int i=1;i<=n;i++)
     for (int j=a[i];j<=m;j++)
      f[j]+=f[j-a[i]];
    printf("%lld\n",f[m]);
    getchar();
    getchar();
    return 0;
}

2.3.5.Controlling Companies

思路:

这道题正解是深搜,但是暴力也是过的。
看代码吧。。。

代码:

#include 
#include 
using namespace std;

const int N=110;
int m,n,x,y,z,K[N][N],sum[N],SUM;
	//K[i][j]表示公司i占公司j的股份
bool q[N][N],ap[N];

struct answer
{
    int x,y;
}ans[N*N];

struct node
{
    int x,s;
}p[N][N];

bool cmp(answer x,answer y)
{
    if (x.x<y.x) return 1;
    if (x.x>y.x) return 0;
    if (x.y<y.y) return 1;
    return 0;
}

int main()
{
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        ap[x]=ap[y]=1;
        K[x][y]=z;
        p[x][++sum[x]].x=y;
        p[x][sum[x]].s=z;
    }
    for (int i=1;i<=100;i++)
        for (int l=1;l<=100;l++)
            for (int j=1;j<=100;j++)  //时间复杂度平摊后为n^3
                if (ap[i]&&ap[j]&&i!=j&&(!q[i][j])&&K[i][j]>50)
       			{
       	   			q[i][j]=1;
       	   			ans[++SUM].x=i;
       	   			ans[SUM].y=j;
       	   			for (int k=1;k<=sum[j];k++)
       	    			K[i][p[j][k].x]+=p[j][k].s;
       	   			break;  //break使得复杂度平摊
       			}
    sort(ans+1,ans+1+SUM,cmp);
    for (int i=1;i<=SUM;i++)
     	printf("%d %d\n",ans[i].x,ans[i].y);
    return 0;
}

你可能感兴趣的:(USACO解题报告)