P8186 [USACO22FEB] Redistributing Gifts S 总结

思路历程:

一开始的确有想过跟图论相关的写法,但是因为没能很好的确定礼物之间贪心的优先关系,所以这个方法我没能实现。

粗略看了下题解,发现可以用邻接表先进先看的性质解决这个问题,我们考虑到每个奶牛都会初始拿到和自己编号一致的礼物,所以我们考虑每一个奶牛都对他们愿望单里的东西进行建边表示关系。

我们继续手推样例不难发现,一只奶牛的选择一定会影响另一只奶牛的礼物,所以我们可以想到一个结论,那就是交换礼物使得结果更优的一定是一群奶牛互换,放在图里就是环的形成。

也就是说我们只需要在图里头找到环就行,如果一个奶牛没有加入到任何一个环中,那么保持原状就是最好的结果。

同时我们建图的时候使用的是邻接表,这保证了奶牛的目标选择一定是按照自己愿望单上的顺序的,其实链式前向星也可以,但是建图的时候需要反序建边。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int N = 1e5+100;

void solve () {
    int n;
    cin >> n;
    vector <vector <int>> a(n+1, vector <int>(n+1));
    vector <vector <int>> d(n+1, vector <int>(n+1));
    vector <int> E[510]; 
    for (int i = 1;i <= n;i++) 
        for (int j = 1;j <= n;j++) {
            cin >> a[i][j];
            if (a[i][j] == i) {
                for (j++;j <= n;j++) cin >> a[i][j]; 
                break;
            }
            d[i][a[i][j]] = true;
            E[i].push_back(a[i][j]); 
        }
    for (int k = 1;k <= n;k++) 
        for (int i = 1;i <= n;i++)
            for (int j = 1;j <= n;j++)
                d[i][j] |= (d[i][k] && d[k][j]);
    for (int i = 1;i <= n;i++) {
        bool flag = false;
        for (auto to : E[i]) 
            if (d[to][i]) { cout << to << "\n"; flag = true; break; }
        if (!flag) cout << i << "\n";
    }
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1;
    while (_--) solve();
    return 0;
}